1. Introduction

This notebook contains a differential gene expression analysis and a preliminary thresholded over-representation analysis for a selected dataset from the GEO database. The dataset in particular is GSE157194, which is associated with a publication titled “Atopic dermatitis displays stable and dynamic skin transcriptome signatures” (Möbus et al., 2021).

The goal of the study was to understand the effect of various treatments for atopic dermatitis by conducting a gene expression study using mRNA-Seq data taken from biopsies of the affected and non-affected skin of human patients. The dataset contains data for 57 patients, with samples of affected (intrapersonal lesional) and non-affected (non-lesional) skin, along with follow-ups after three months of treatment (with either dupilumab or cyclosporine). An important note is that the dataset is “incomplete”/has several prominent issues:

No information regarding these missing features were found in the associated paper.

After downloading the raw count matrix from GEO via GEOquery (Davis & Meltzer, 2007), the data was cleaned (confirming that there were no duplicate genes), filtered by gene (removing those without at least one read per million in each sample), and filtered by patient (removing those with incomplete data; see the points above). The genes (rows) were labelled by their Ensembl gene IDs, which were mapped to HUGO gene symbols using biomaRt (Durinck et al., 2009), and genes that were unable to be converted (such as novel genes) were removed. To normalize the data, trimmed mean of m-values/TMM was used (Robinson et al., 2010), since we followed the assumption that most genes are not differentially expressed. Finally, the data was checked for outliers (none were found), and the resulting data frame was written to a tab-delimited txt file (and gzipped for upload to GitHub).

We can load the dataset here and take a look at the first few rows and columns:

# The dataset should originally be compressed (GitHub 25 MB)
filepath <- file.path(getwd(), "normalizedCountData.txt")
if (!file.exists(filepath)) {
  gzfilepath <- file.path(getwd(), "normalizedCountData.txt.gz")
  if (!file.exists(gzfilepath)) {
    stop("Dataset not found")
  }
  GEOquery::gunzip(gzfilepath)
}

normalizedCountData <- read.table(file = file.path(getwd(), "normalizedCountData.txt"),
                                  sep = "\t",
                                  header = TRUE,
                                  stringsAsFactors = FALSE,
                                  check.names = FALSE)
knitr::kable(normalizedCountData[1:4, 1:4], type = "html")
Patient_1_AN_m0 Patient_1_AL_m0 Patient_3_AN_m0 Patient_3_AL_m0
A1BG-AS1 1.216988 1.0737920 1.901915 2.7945041
A2M 213.360038 263.8843886 386.515720 303.5713893
A2M-AS1 1.880799 0.9203932 2.057173 0.6618562
A2ML1 131.268699 143.3512345 156.034665 127.2234747

Number of patients: 24
Number of samples: 96
Number of genes: 17747

The column names are formatted “Patient_<patient number>_<AN|AL>_<m0|m3>“, where AN means intrapersonal non-lesional, AL means intrapersonal lesional, m0 means the sample was taken before treatment, and m3 means the sample was taken after three months of treatment. Note the absence of patient 2 between patients 1 and 3, who was removed due to missing data.

2. Differential gene expression

The corresponding journal entry for this assignment can be found here.

First, we should inspect the data as a heatmap to get a general idea of gene expression levels. But before we do that, we should row-normalize it, which scales rows and centres them around the mean, so that no particular gene completely dominates the plot.

# Parameters to select the range of samples to use. The matrix has 17,747 rows and 96 columns, and if too much data is
# chosen, the code will crash
SAMPLE_START <- 1
# Setting SAMPLE_END to 12 or more has been tested to crash
SAMPLE_END <- 8
heatmapMatrix <- normalizedCountData[, SAMPLE_START:SAMPLE_END]

# Row-normalization can be done with the base scale function on the transposed matrix
heatmapMatrix <- t(scale(t(heatmapMatrix)))
heatmapMatrix[is.na(heatmapMatrix)] <- 0

if (min(heatmapMatrix) == 0) {
  # No negative values => colours should only range from white to red
  heatmapColours <- circlize::colorRamp2(c(0, max(heatmapMatrix)),
                                         c("white", "red"))
} else {
  # Has negative values => use blue for those
  heatmapColours <- circlize::colorRamp2(c(min(heatmapMatrix), 0, max(heatmapMatrix)),
                                         c("blue", "white", "red"))
}

currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(heatmapMatrix),
                                       show_row_dend = TRUE,
                                       show_column_dend = FALSE,
                                       col = heatmapColours,
                                       show_column_names = TRUE,
                                       show_row_names = FALSE,
                                       show_heatmap_legend = TRUE,
                                       cluster_columns = FALSE,
                                       name = "Key",
                                       column_title = "Normalized heatmap of samples")

# Displaying the heatmap may take a while to run, or just crash
currHeatmap

By default (and only if the last line in the code snippet directly above is not commented out), the heatmap will be displayed for eight samples (four patients, both before treatment). The parameters at the start of the code snippet can be changed to display different parts of the entire heatmap. Regardless, we can already see that there is significant variation in the data between different patients. This will need to be taken into account when doing further analysis.

Testing a single gene

The authors of the paper hypothesized that IL13 (interleukin 13, a gene related to type 2 cytokines and chemokines), among others, were responsible for the lesions found in patients with AD, so we can run a quick test to see if there is any change in expression for this particular gene among the entire set of patients.

# Get the columns corresponding to pre- and post-treatment affected samples
preTreatAffSamples <- grep(colnames(normalizedCountData), pattern = "_AL_m0")
postTreatAffSamples <- grep(colnames(normalizedCountData), pattern = "_AL_m3")

# Find the row index of IL13 in the data
il13Idx <- which(rownames(normalizedCountData) == "IL13")

# Format the expression levels (for both pre- and post-treatment) in a table for comparison
il13Expr <- t(normalizedCountData[il13Idx, preTreatAffSamples])
il13Expr <- as.data.frame(il13Expr)
il13Expr <- cbind(il13Expr, t(normalizedCountData[il13Idx, postTreatAffSamples]))
colnames(il13Expr) <- c("Pre-treatment", "Post-treatment")

# A visual inspection of some of the data may be interesting
knitr::kable(il13Expr[1:8, ], type = "html")
Pre-treatment Post-treatment
Patient_1_AL_m0 0.2684480 0.2614197
Patient_3_AL_m0 0.2206187 1.0196571
Patient_4_AL_m0 0.8283549 0.0968785
Patient_7_AL_m0 3.1572718 0.3413900
Patient_8_AL_m0 0.1581532 0.2282131
Patient_9_AL_m0 0.2365710 4.0256386
Patient_11_AL_m0 0.2784309 0.9275250
Patient_12_AL_m0 0.4850444 0.7312003

Looking at the expression change may not necessarily be conclusive, so we can also try a simple t-test.

t.test(x = t(il13Expr$`Pre-treatment`), y=t(il13Expr$`Post-treatment`))

    Welch Two Sample t-test

data:  t(il13Expr$`Pre-treatment`) and t(il13Expr$`Post-treatment`)
t = -0.85029, df = 42.098, p-value = 0.4
alternative hypothesis: true difference in means is not equal to 0
95 percent confidence interval:
 -1.1635641  0.4736868
sample estimates:
mean of x mean of y 
0.8118375 1.1567761 

We see that the results of the test do not suggest a difference in expression before and after treatment for the entire set of patients, which provides evidence to suggest that patient variation is a major factor that needs to be accounted for.

Creating a model

To try and control for these variables, we can use various packages that provide methods specialized for differential expression analysis. In particular, edgeR is a package especially suited for bulk RNA-Seq analysis, containing several linear methods that we can use (Robinson et al., 2010). We start by setting up the counts header into individual categories, which should make it easier to use different parts of the data for different analyses or plots.

# Defines different groups that can be combined for different things (adapted from A1)
splitColNames <- function(colNames) {
  return(unlist(strsplit(colNames, split = "_"))[c(2, 3, 4)])
}
groups <- data.frame(lapply(colnames(normalizedCountData), splitColNames))
colnames(groups) <- colnames(normalizedCountData)
rownames(groups) <- c("Patient", "Sample type", "Sample time")
groups <- data.frame(t(groups))

# Check the first few lines of the whole table to make sure the groups have been defined properly
knitr::kable(groups[1:4, ], type = "html")
Patient Sample.type Sample.time
Patient_1_AN_m0 1 AN m0
Patient_1_AL_m0 1 AL m0
Patient_3_AN_m0 3 AN m0
Patient_3_AL_m0 3 AL m0

Next, we can throw (parts of) the data into an MDS plot in order to get some insight as to how we should set up our linear models.

# Parameters to select which subset of data to plot
PATIENT_START <- 1
PATIENT_END <- 5
# Get the affected subset of the data
affectedCountData <- normalizedCountData[grep(colnames(normalizedCountData), pattern = "_AL_")
                                         ][, c(PATIENT_START:PATIENT_END, (PATIENT_START + 24):(PATIENT_END + 24))]
affectedGroups <- groups[groups$Sample.type == "AL",
                         ][c(PATIENT_START:PATIENT_END, (PATIENT_START + 24):(PATIENT_END + 24)), ]

# Build the MDS plot, colouring by time (pre- and post-treatment)
dgelist <- edgeR::DGEList(counts = affectedCountData, group = affectedGroups$Sample.time)
edgeR::plotMDS.DGEList(dgelist, labels = rownames(affectedGroups),
                       col = c("darkgreen", "blue")[factor(affectedGroups$Sample.time)])
legend(x = "bottomleft", legend = c("Pre-treatment", "Post-treatment"), fill = c("darkgreen", "blue"))
title(main = "Distance between samples, coloured by treatment time")

Also try grouping by patient to see approximately how much variability there is.

# Colour by patient number
patientColours <- rainbow(PATIENT_END - PATIENT_START + 1)
edgeR::plotMDS.DGEList(dgelist, labels = rownames(affectedGroups),
                       col = patientColours)
title(main = "Distance between samples, coloured by patient number")

We see that there is no especially obvious clustering in treatment time or individual patient, which could be explained by the imperfect data; e.g., perhaps the patients shown here used different drugs or some were actually controls. Regardless, there is incentive to try fitting the data to a linear model to see if any improvements can be found.

edgeR provides a convenient quasi-likelihood method that has been recommended for bulk RNA-Seq experiments and is often used for more complicated models. Since the dataset that we are working with is quite large and complex, it may be a good candidate to use. We will first try a straightforward model that does not account for patient variability.

# Create a linear design matrix for edgeR quasi likelihood in treatment time
modelDesign <- model.matrix(~ groups$Sample.time)
dgelist <- edgeR::DGEList(counts = normalizedCountData, group = groups$Sample.time)
dgelist <- edgeR::estimateDisp(dgelist, modelDesign)
modelFit <- edgeR::glmQLFit(dgelist, modelDesign)

# Extract the results
qlfTime <- edgeR::glmQLFTest(modelFit, coef = "groups$Sample.timem3")
qlfHits <- edgeR::topTags(qlfTime, sort.by = "PValue", n = nrow(normalizedCountData))
knitr::kable(edgeR::topTags(qlfTime)[1:8, ], type = "html")
logFC logCPM F PValue FDR
CCL18 -2.5618699 6.650771 50.53415 0 3.50e-06
SPARC 1.3570281 10.440376 45.50124 0 8.60e-06
BCL6 -0.5799921 6.810072 44.66884 0 8.60e-06
CCL3-AS1 -1.8888323 1.666397 43.34914 0 1.03e-05
CCL13 -1.5892900 5.469213 40.18952 0 2.55e-05
FKBP5 -0.9104746 7.304668 39.31521 0 2.92e-05
APLNR 0.9643666 4.052355 38.77831 0 3.04e-05
NPTX2 -1.2510772 2.645831 38.09414 0 3.42e-05
x
BH
x
groups$Sample.timem3
x
glm

Number of genes that pass the threshold p-value (0.05): 2541
Number of genes that pass correction (FDR): 280

Since we are doing many tests, the probability that a positive result will occur simply by chance is increased. To control for this, we can use a method of multiple hypothesis testing, which will try to correct the positivity rate. edgeR’s quasi-likelihood method does this by using false discovery rate, a fairly common way to correct the data.

We see that a fairly large number of genes are differentially expressed, even after correction with FDR, so an even stronger threshold can be considered. This will allow for greater levels of confidence when analyzing the data, and will also put a greater emphasis on which genes can be considered to be more important because they are significantly differentially expressed.

Number of genes that pass a stricter p-value (0.01): 976
Number of genes that pass correction (FDR): 94

Even stricter thresholds could be considered (such as 0.001), but we do not want to completely throw away the weaker signal. We can next try to fit the data to a model that incorporates patient number into it, which may have better results in downstream analysis.

# Essentially the same as before, but with additional data
modelDesignPatient <- model.matrix(~ groups$Patient + groups$Sample.time)
dgelist <- edgeR::DGEList(counts = normalizedCountData, group = groups$Sample.time)
dgelist <- edgeR::estimateDisp(dgelist, modelDesignPatient)
modelFit <- edgeR::glmQLFit(dgelist, modelDesignPatient)

qlfTime <- edgeR::glmQLFTest(modelFit, coef = "groups$Sample.timem3")
qlfHitsPatient <- edgeR::topTags(qlfTime, sort.by = "PValue", n = nrow(normalizedCountData))
knitr::kable(edgeR::topTags(qlfTime[1:8, ]), type = "html")
logFC logCPM F PValue FDR
A2ML1 -0.4733038 7.791353 13.1409656 0.0005242 0.0041934
A2M 0.1952677 8.021805 4.0324669 0.0482401 0.1929606
AAAS -0.0618142 4.961146 2.6766718 0.3108878 0.4984121
A2M-AS1 0.2207703 1.697648 5.2621708 0.3623892 0.4984121
A4GALT 0.0833681 4.645910 0.7130782 0.4011140 0.4984121
AACSP1 -0.2199525 1.581059 2.9539634 0.4035144 0.4984121
AACS 0.0655970 6.777246 0.6130520 0.4361106 0.4984121
A1BG-AS1 0.0373999 1.985611 0.1546446 0.8453775 0.8453775
x
BH
x
groups$Sample.timem3
x
glm

Number of genes that pass the threshold p-value (0.05): 3089
Number of genes that pass correction (FDR): 719

Number of genes that pass a stricter p-value (0.01): 1414
Number of genes that pass correction (FDR): 267

When applying a model that takes patient variability into account, we see that the number of genes that are significantly differentially expressed increases, which is not unexpected.

Genes of interest

A quick next step is to try and find genes of interest; e.g., those that are the most differentially expressed. One method of accomplishing this task is to use a volcano plot, which puts p-value against log-fold change. In this way, genes with the most meaningful or significant changes will be found near the top-left and top-right corners of the graph. Here, the R package EnhancedVolcano is used to quickly throw together such a plot (Blighe et al., 2021).

# Get the log-fold change and p-values from the patient-variability-included model
volcano <- data.frame(log2FC = qlfTime$table$logFC, PValue = qlfTime$table$PValue)
rownames(volcano) <- rownames(normalizedCountData)
EnhancedVolcano::EnhancedVolcano(volcano, lab = rownames(volcano), x = "log2FC", y = "PValue",
                                 title = "Differentially expressed genes")

We see that the vast majority of genes (17,747 in total) are not significantly differentially expressed, being plotted toward the bottom-centre of the graph, but there are a few genes that present themselves as of interest. For example, the plot identifies CCL18, IL19, NPTX2, ADORA3, ALOX15, SPARC, ACSM6, and SPINK9 as genes with significant differences in levels of expression.

The next step is to return to looking at heatmaps; we can now plot the results of applying the quasi-likelihood model to the data, which may result in clearer clusters that can be visually identified.

# Using the stricter p-value threshold
topHits <- rownames(qlfHits$table)[qlfHits$table$PValue < 0.01]
# Warning: heatmapMatrix may only have been built with a subset of the data. topHeatmapMatrix here will reflect that
# subset. Change SAMPLE_START and SAMPLE_END in the code snippet at the start of this section to change the selected
# data
topHeatmapMatrix <- t(scale(t(heatmapMatrix[which(rownames(heatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0

if (min(topHeatmapMatrix) == 0) {
  heatmapColours <- circlize::colorRamp2(c(0, max(topHeatmapMatrix)),
                                         c("white", "red"))
} else {
  heatmapColours <- circlize::colorRamp2(c(min(topHeatmapMatrix), 0, max(topHeatmapMatrix)),
                                         c("blue", "white", "red"))
}

groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.type)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Type = groups$Sample.type[SAMPLE_START:SAMPLE_END]),
                                                 col = list(type = groupColours))

currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
                                       show_row_dend = TRUE,
                                       show_column_dend = TRUE,
                                       col = heatmapColours,
                                       show_column_names = TRUE,
                                       show_row_names = FALSE,
                                       show_heatmap_legend = TRUE,
                                       cluster_columns = FALSE,
                                       name = "Key",
                                       column_title = "Top hits heatmap",
                                       top_annotation = heatmapAnno)

currHeatmap

This heatmap is not particularly useful in determining if differentially expressed genes cluster, because the columns are ordered such that individual patient data is paired together. For example, the first column here is the control sample for the first patient (the non-affected skin) pre-treatment, and the second column is the affected skin sample, also pre-treatment. It should be possible to get a better idea of clustering over time by reordering the columns.

# Build a new heatmap matrix to decouple from SAMPLE_START and SAMPLE_END
reHeatmapMatrix <- normalizedCountData
reHeatmapMatrix <- t(scale(t(reHeatmapMatrix)))
reHeatmapMatrix[is.na(reHeatmapMatrix)] <- 0

# Don't know how to do this in a one-liner
newIdxs <- integer(SAMPLE_END - SAMPLE_START + 1)
currIdx <- 1
# Add the *_AN_m0 first
for (i in seq(SAMPLE_START, SAMPLE_END, 2)) {
  newIdxs[currIdx] <- i
  currIdx <- currIdx + 1
}
# Then add the *_AN_m3
for (i in seq(SAMPLE_START, SAMPLE_END, 2)) {
  newIdxs[currIdx] <- i + 48
  currIdx <- currIdx + 1
}

topHeatmapMatrix <- t(scale(t(reHeatmapMatrix[which(rownames(reHeatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0
topHeatmapMatrix <- topHeatmapMatrix[, newIdxs]

groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.time)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Time = groups$Sample.time[newIdxs]),
                                                 col = list(type = groupColours))

currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
                                       show_row_dend = TRUE,
                                       show_column_dend = TRUE,
                                       col = heatmapColours,
                                       show_column_names = TRUE,
                                       show_row_names = FALSE,
                                       show_heatmap_legend = TRUE,
                                       cluster_columns = FALSE,
                                       name = "Key",
                                       column_title = "Top hits, control, over time",
                                       top_annotation = heatmapAnno)

currHeatmap

The above heatmap has been rearranged such that all eight columns are control (non-affected/non-lesional) skin of affected patients, with the left four columns being samples taken just prior to treatment, and the right four samples being taken after three months of treatment. Since this subset of the dataset does not contain any samples of affected (lesional) skin, it is not surprising that levels of gene expression are similar across the two categories. The most obvious difference would be with respect to patient 3 (and patient 1, to a certain extent), where the follow-up sample has greater levels of regulation–but it is still of the same type.

# Doing the same as above except with *_AL_* instead of *_AN_*
newIdxs <- integer(SAMPLE_END - SAMPLE_START + 1)
currIdx <- 1
for (i in seq(SAMPLE_START + 1, SAMPLE_END + 1, 2)) {
  newIdxs[currIdx] <- i
  currIdx <- currIdx + 1
}
for (i in seq(SAMPLE_START + 1, SAMPLE_END + 1, 2)) {
  newIdxs[currIdx] <- i + 48
  currIdx <- currIdx + 1
}

topHeatmapMatrix <- t(scale(t(reHeatmapMatrix[which(rownames(reHeatmapMatrix) %in% topHits), ])))
topHeatmapMatrix[is.na(topHeatmapMatrix)] <- 0
topHeatmapMatrix <- topHeatmapMatrix[, newIdxs]

groupColours <- c("blue", "red")
names(groupColours) <- unique(groups$Sample.time)
heatmapAnno <- ComplexHeatmap::HeatmapAnnotation(df = data.frame(Time = groups$Sample.time[newIdxs]),
                                                 col = list(type = groupColours))

currHeatmap <- ComplexHeatmap::Heatmap(as.matrix(topHeatmapMatrix),
                                       show_row_dend = TRUE,
                                       show_column_dend = TRUE,
                                       col = heatmapColours,
                                       show_column_names = TRUE,
                                       show_row_names = FALSE,
                                       show_heatmap_legend = TRUE,
                                       cluster_columns = FALSE,
                                       name = "Key",
                                       column_title = "Top hits, over time",
                                       top_annotation = heatmapAnno)

currHeatmap

When we look at the heatmap concerning affected skin, the differential expression is more obvious and more significant. There are many clusters of genes on the right side of the plot that correspond inversely to the same region on the left side of the plot. For example, the column of patient 7 has a large, strongly expressed group of genes near the middle of the column. However, when looking at the same area on the left side of the heatmap, the expression of the same genes is very weak in comparison. Thus, we can hypothesize that perhaps these differentially expressed genes may be responsible for AD and/or its symptoms.

3. Thresholded over-representation analysis

In this next part of the notebook, the sets of significantly up-regulated and down-regulated genes will be run through a thresholded gene set enrichment analysis. The first step is to get the lists of such genes.

# Construct the ranked gene list (non-thresholded)
qlfHitsT <- as.data.frame(qlfHitsPatient)
qlfHitsT["rank"] <- -log(qlfHitsT$PValue, base = 10) * sign(qlfHitsT$logFC)
qlfHitsT <- qlfHitsT[order(qlfHitsT$rank), ]

write.table(data.frame(genename = rownames(qlfHitsT), Fstat = qlfHitsT$rank),
            file = file.path(getwd(), "rankedGenelist.txt"),
            sep = "\t",
            row.names = FALSE,
            col.names = FALSE,
            quote = FALSE)

# For the thresholded lists, use p = 0.05 instead of 0.01 to get more results
upregGenes <- rownames(qlfHitsT[which(qlfHitsT$PValue < 0.05 & qlfHitsT$logFC > 0), ])
write.table(upregGenes,
            file = file.path(getwd(), "upregulatedGenes.txt"),
            sep = "\t",
            row.names = FALSE,
            col.names = FALSE,
            quote = FALSE)

downregGenes <- rownames(qlfHitsT[which(qlfHitsT$PValue < 0.05 & qlfHitsT$logFC < 0), ])
write.table(downregGenes,
            file = file.path(getwd(), "downregulatedGenes.txt"),
            sep = "\t",
            row.names = FALSE,
            col.names = FALSE,
            quote = FALSE)

The technique that has been chosen to perform this analysis is Fisher’s exact test, done through g:Profiler (Kolberg et al., 2020). Fisher’s exact test is a common statistical method to check for overrepresentation, which is the goal of this analysis, and is able to calculate the deviation from p-values exactly. g:Profiler is a common, convenient (accessible online and through an R package) implementation of Fisher’s exact test that is updated fairly regularly, which can help increase the accuracy of the analysis. For our purpose, and in the interest of time, g:Profiler will be accessed through its website (instead of using the R package).

The annotation datasets that have been selected include GO biological processes (updated 2021-12-15) - without electronic annotations, Reactome (updated 2022-01-03), and WikiPathways (updated 2021-12-10). These three datasets are major annotation resources, and have been updated fairly recently, so it is reasonable to expect accuracte results. Other major resources such as KEGG have not been included because they may add additional extraneous information to the results, and with these three datasets, there should already be enough information for a thorough analysis.

We will start by looking at subsets of the genes of interest (i.e., those with significant differential expression), and then follow up by analyzing the entire set to compare how looking at different parts of the data may affect the results. For each run, FDR will be used for multiple hypothesis correction, with a threshold p-value of 0.05. This was chosen instead of the more stringent 0.01 to allow for more information and a broader look at the associated pathways. In addition, the resulting pathways were filtered to only include those with gene set sizes of 200 or less. This is because of the inheritance-like property of the annotation resources, which may clutter the results by including pathways that are too generic.

Upregulated genes

The first subset is all upregulated genes with significant differential expression. The list of upregulated genes (from the code snippet above) was copied to g:Profiler, the mentioned options were set (with all results checked), and the analysis was run. The result was the following figure:

Figure 1. upregulated genes only

g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of upregulated genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.

Using the thresholds mentioned previously, GO:BP found 5,027 pathways, REAC found 1,007, and WP found 445. The plot at the top of the figure is a visual representation of adjusted p-values vs. resource, where each dot represents a specific pathway (each annotation resource has its own colour). The table at the bottom of the figure is a list of the top (approximately 15) pathways, according to adjusted p-value, for each resource. The individual pathways are given numbers, and these correspond to the labelling of the dots in the plot above. We see that despite being the pathways with the greatest adjusted p-value, these labelled dots are not the highest on the plot because of the thresholds that were used; the unlabelled dots would represent more generic pathways.

The specific pathways that were found here–for example, collagen fibril organization and several fatty acid metabolic processes–are not obviously indicative of AD, a skin disease of autoimmune origin.

Downregulated genes

The second subset is all downregulated genes with significant differential expression. The list of downregulated genes was copied to g:Profiler, options were set (this time without all results, since the list of genes was too large), and the analysis was run. The result was the following figure:

Figure 2. downregulated genes only

g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of downregulated genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.

Using the thresholds mentioned previously, GO:BP found 230 pathways, REAC found 72, and WP found 52. The specific pathways that were found here–for example, various DNA replication pathways and cell cycle signalling–is, like the upregulated genes, not directly related to AD. However, these examples are not as broad, and it is possible that there is an indirect link. For example, psoriasis, another skin condition similar to atopic dermatitis, is directly related to the disregulation of the cell cycle of skin cells, so it would not be a surprise to see a similar result here.

All differentially expressed genes

It is important to consider the entire set of differentially expressed genes as a whole, because it is possible that the particular pathways that we are expecting have both upregulated and downregulated genes. The result of running g:Profiler on the list of combined genes is as follows:

Figure 3. all differentially expressed genes

g:Profiler has omitted titles for the plot and table, but appropriate ones would be “Over-representation of significant differentially expressed genes with p-value < 0.05” and “Highest scoring pathways for gene sets with size less than 200,” respectively.

Using the thresholds mentioned previously, GO:BP found 130 pathways, REAC found 37, and WP found 15. When we put both the upregulated genes and downregulated genes together through over-representation analysis, it appears that we get a better overall image of the pathways that AD affects. For example, the top GO:BP hit is now directly related to skin development, an obvious link to AD. In addition, we see other pathways related to immune function; for example, the Toll-like receptor 4 (TLR4) cascade, which is responsible for the production of cytokines.

Since many of these pathways were not present when only the up- or downregulated genes were considered, it is possible to conclude that AD is involved in more complex pathways that cannot be restricted to just one type of gene. The entire set of differentially expressed genes needs to be considered, and only then will the relevant pathways appear.

4. Interpretation

With regard to the differential expression analysis, several of the most significant genes that were found were also found by the authors of the original paper. For example, one with a particularly high level of expression was IL19, with the paper stating that they “detected a notable expression of IL19 … in AL,” exactly what was found here. However, this analysis did find some highly expressed genes that were not of note in the paper; for example, NPTX2, related to the C-reactive protein, was not noted in the paper. Additionally, other genes like IL31 and IL23 that were noted in the paper were not among the most expressed genes here. Thus, we can say that for this part of the notebook, the results are at least partially supported by the original paper.

When looking at pathways, the result of over-representation analysis, we see some similarities between those that were found here and those that were found in the original paper. The paper states that the “core transcriptome signature” of AD patients is characterized by epidermal differentiation and itch pathways, and gives sebaceous glands as an example. In the ORA of all differentially expressed genes, the highest scored pathway is related to skin development, of which “sebaceous gland development” is a child.

Additional pathways that were found in both include general Toll-like receptor signalling, interleukin signalling, and several other immune-related pathways. However, the paper does mention specific examples like Th17 cell differentiation and the IL-17 signalling pathway, which were not exactly found in the ORA. Thus, we should be able to conclude that for this part of the notebook, the results overall are supported by the original paper, but in order to confirm the presence of more specific pathways, more detailed examination must be done.

When considering the differentially expressed genes and pathways, another way of looking for evidence to support that the analyses in this notebook (other than comparing with the original paper) is to look for additional papers that have reached the same conclusion. One study (Oka et al., 2015) showed that IL-19, one of the most significantly expressed interleukin genes found here, was notably increased in patients with AD. Statistical analysis (primarily done via the Mann-Whitney U test) was performed, and in summary, concluded that there was statistically significant evidence to suggest that AD patients had elevated IL-19 protein levels, a reflection of significant expression.

A second paper (Panzer et al., 2014) found that AD patients also have significant levels of TLR4 expression as part of Toll-like receptor signalling pathways. Samples from lesional skin were taken, incubated with TLR4 antibodies, and then RNA was extracted, which was found to show constitutive expression of TLR4. There have even been previous studies on using dupilumab and cyclosporine to treat AD; a thid paper (Mansouri & Guttman-Yassky, 2015) specifically studied the effects of these drugs on human immune pathways, and found that they are able to block some interleukin signalling pathways, which in turn resulted in clinical improvements. Thus, this shows that there is further support for the pathways discovered by the ORA conducted here.

5. References

Blighe, K., Rana, S., & Lewis, M. (2021). EnhancedVolcano: Publication-ready volcano plots with enhanced colouring and labeling. https://github.com/kevinblighe/EnhancedVolcano
Davis, S., & Meltzer, P. (2007). GEOquery: A bridge between the gene expression omnibus (GEO) and BioConductor. Bioinformatics, 23(14), 1846–1847. https://doi.org/10.1093/bioinformatics/btm254
Durinck, S., Spellman, P. T., Birney, E., & Huber, W. (2009). Mapping identifiers for the integration of genomic datasets with the r/bioconductor package biomaRt. Nat Protoc, 4(8), 1184–1191. https://doi.org/ doi: 10.1038/nprot.2009.97
Gu, Z., Eils, R., & Schlesner, M. (2016). Complex heatmaps reveal patterns and correlations in multidimensional genomic data. Bioinformatics, 32(18), 2847–2849. https://doi.org/10.1093/bioinformatics/btw313
Gu, Z., Gu, L., Eils, R., Schlesner, M., & Brors, B. (2014). Circlize implements and enhances circular visualization in r. Bioinformatics, 30(19), 2811–2812. https://doi.org/10.1093/bioinformatics/btu393
Kolberg, L., Raudvere, U., Kuzmin, I., Vilo, J., & Peterson, H. (2020). gprofiler2 – an r package for gene list functional enrichment analysis and namespace conversion toolset g:profiler. F1000Res, 9(709). https://doi.org/10.12688/f1000research.24956.2
Mansouri, Y., & Guttman-Yassky, E. (2015). Immune pathways in atopic dermatitis, and definition of biomarkers through broad and targeted therapeutics. J Clin Med, 4(5), 858–873. https://doi.org/10.3390/jcm4050858
Möbus, L., Rodriguez, E., Harder, I., Stölzl, D., Boraczynski, N., Gerdes, S., Kleinheinz, A., Abraham, S., Heratizadeh, A., Handrick, C., Haufe, E., Werfel, T., Schmitt, J., Weidinger, S., & the TREATgermany study group. (2021). Atopic dermatitis displays stable and dynamic skin transcriptome signatures. J Allergy Clin Immun, 147(1), 213–223. https://doi.org/10.1016/j.jaci.2020.06.012
Morgan, M. (2021). BiocManager: Access the bioconductor project package repository. https://CRAN.R-project.org/package=BiocManager
Oka, T., Sugaya, M., Takahashi, N., Nakajima, R., Otobe, S., Kabasawa, M., Suga, H., Miyagaki, T., Asano, Y., & Sato, S. (2015). Increased interleukin-19 expression in cutaneous t-cell lymphoma and atopic dermatitis. Acta Derm Venereol, 97(10), 1172–1177. https://doi.org/10.2340/00015555-2723
Panzer, R., Blobel, C., Fölster-Holst, R., & Proksch, E. (2014). TLR2 and TLR4 expression in atopic dermatitis, contact dermatitis and psoriasis. Exp Dermatol, 23(5), 364–366. https://doi.org/10.1111/exd.12383
Robinson, M. D., McCarthy, D. J., & Smyth, G. K. (2010). edgeR: A bioconductor package for differential expression analysis of digital gene expression data. Bioinformatics, 26(1), 139–140. https://doi.org/10.1093/bioinformatics/btp616
Xie, Y. (2021). Knitr: A general-purpose package for dynamic report generation in r. https://yihui.org/knitr
LS0tCnRpdGxlOiAiQkNCNDIwIEEyIC0gRGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbiBhbmQgcHJlbGltaW5hcnkgT1JBIgphdXRob3I6ICJZaWppYSBDaGVuIgpkYXRlOiAiMjAyMi0wMy0xNiIKb3V0cHV0OgogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKYmlibGlvZ3JhcGh5OiBBMl9jaXRhdGlvbnMuYmliCmNzbDogYXBhLmNzbApub2NpdGU6ICJAKiIKLS0tCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0UsIGluY2x1ZGUgPSBGQUxTRX0KIyBTZXZlcmFsIFIgcGFja2FnZXMgYXJlIHJlcXVpcmVkIHRvIHJ1biB0aGlzIG5vdGVib29rLiBUaGV5IGNhbiBiZSBpbnN0YWxsZWQgYnkgcnVubmluZyB0aGlzIHNuaXBwZXQKIyBUaGlzIHNuaXBwZXQgd2lsbCBub3QgYmUgaW5jbHVkZWQgaW4gdGhlIGZpbmFsIHJlcG9ydCBiZWNhdXNlIHRoZSBpbnN0YWxsYXRpb24gb2YgRW5oYW5jZWRWb2xjYW5vIHdpbGwgYWx3YXlzCiMgZ2VuZXJhdGUgb3V0cHV0IHRoYXQgY2Fubm90IGJlIHN1cHByZXNzZWQgYnkgbWVzc2FnZSA9IEZBTFNFIG9yIHdhcm5pbmcgPSBGQUxTRQoKaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJCaW9jTWFuYWdlciIsIHF1aWV0bHkgPSBUUlVFKSkgewogIGluc3RhbGwucGFja2FnZXMoIkJpb2NNYW5hZ2VyIikKfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoIkdFT3F1ZXJ5IiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgQmlvQ01hbmFnZXI6Omluc3RhbGwoIkdFT3F1ZXJ5IikKfQppZiAoIXJlcXVpcmVOYW1lc3BhY2UoImtuaXRyIiwgcXVpZXRseSA9IFRSVUUpKSB7CiAgaW5zdGFsbC5wYWNrYWdlcygia25pdHIiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiZWRnZVIiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9DTWFuYWdlcjo6aW5zdGFsbCgiZWRnZVIiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiQ29tcGxleEhlYXRtYXAiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiQ29tcGxleEhlYXRtYXAiKQp9CmlmICghcmVxdWlyZU5hbWVzcGFjZSgiY2lyY2xpemUiLCBxdWlldGx5ID0gVFJVRSkpIHsKICBpbnN0YWxsLnBhY2thZ2VzKCJjaXJjbGl6ZSIpCn0KaWYgKCFyZXF1aXJlTmFtZXNwYWNlKCJFbmhhbmNlZFZvbGNhbm8iLCBxdWlldGx5ID0gVFJVRSkpIHsKICBCaW9jTWFuYWdlcjo6aW5zdGFsbCgiRW5oYW5jZWRWb2xjYW5vIiwgdXBkYXRlID0gRkFMU0UsIGFzayA9IEZBTFNFKQp9CmxpYnJhcnkoRW5oYW5jZWRWb2xjYW5vKQpgYGAKCiMjIDEuIEludHJvZHVjdGlvbgoKVGhpcyBub3RlYm9vayBjb250YWlucyBhIGRpZmZlcmVudGlhbCBnZW5lIGV4cHJlc3Npb24gYW5hbHlzaXMgYW5kIGEgcHJlbGltaW5hcnkgdGhyZXNob2xkZWQgb3Zlci1yZXByZXNlbnRhdGlvbgphbmFseXNpcyBmb3IgYSBzZWxlY3RlZCBkYXRhc2V0IGZyb20gdGhlIEdFTyBkYXRhYmFzZS4gVGhlIGRhdGFzZXQgaW4gcGFydGljdWxhciBpcwpbR1NFMTU3MTk0XShodHRwczovL3d3dy5uY2JpLm5sbS5uaWguZ292L2dlby9xdWVyeS9hY2MuY2dpP2FjYz1HU0UxNTcxOTQpLCB3aGljaCBpcyBhc3NvY2lhdGVkIHdpdGggYSBwdWJsaWNhdGlvbgp0aXRsZWQgIkF0b3BpYyBkZXJtYXRpdGlzIGRpc3BsYXlzIHN0YWJsZSBhbmQgZHluYW1pYyBza2luIHRyYW5zY3JpcHRvbWUgc2lnbmF0dXJlcyIgW0Btb2J1c10uCgpUaGUgZ29hbCBvZiB0aGUgc3R1ZHkgd2FzIHRvIHVuZGVyc3RhbmQgdGhlIGVmZmVjdCBvZiB2YXJpb3VzIHRyZWF0bWVudHMgZm9yIGF0b3BpYyBkZXJtYXRpdGlzIGJ5IGNvbmR1Y3RpbmcgYSBnZW5lCmV4cHJlc3Npb24gc3R1ZHkgdXNpbmcgbVJOQS1TZXEgZGF0YSB0YWtlbiBmcm9tIGJpb3BzaWVzIG9mIHRoZSBhZmZlY3RlZCBhbmQgbm9uLWFmZmVjdGVkIHNraW4gb2YgaHVtYW4gcGF0aWVudHMuIFRoZQpkYXRhc2V0IGNvbnRhaW5zIGRhdGEgZm9yIDU3IHBhdGllbnRzLCB3aXRoIHNhbXBsZXMgb2YgYWZmZWN0ZWQgKGludHJhcGVyc29uYWwgbGVzaW9uYWwpIGFuZCBub24tYWZmZWN0ZWQKKG5vbi1sZXNpb25hbCkgc2tpbiwgYWxvbmcgd2l0aCBmb2xsb3ctdXBzIGFmdGVyIHRocmVlIG1vbnRocyBvZiB0cmVhdG1lbnQgKHdpdGggZWl0aGVyIGR1cGlsdW1hYiBvciBjeWNsb3Nwb3JpbmUpLiBBbgppbXBvcnRhbnQgbm90ZSBpcyB0aGF0IHRoZSBkYXRhc2V0IGlzICJpbmNvbXBsZXRlIi9oYXMgc2V2ZXJhbCBwcm9taW5lbnQgaXNzdWVzOgoKLSBUaGVyZSBpcyBubyBkaXN0aW5jdGlvbiBiZXR3ZWVuIHBhdGllbnRzIGFuZCBjb250cm9scyAob3IgdGhlIGNvbnRyb2wgcGF0aWVudHMgd2VyZSBvbWl0dGVkLCB3aGljaCBpcyBsaWtlbHkpCi0gTm90IGFsbCBwYXRpZW50cyBoYXZlIHNhbXBsZXMgZm9yIGJvdGggYWZmZWN0ZWQgYW5kIG5vbi1hZmZlY3RlZCBza2luCi0gTm90IGFsbCBwYXRpZW50cyBoYXZlIGZvbGxvdy11cCBkYXRhIGFmdGVyIHRyZWF0bWVudAotIE5vIHBhdGllbnRzIGFyZSBsYWJlbGxlZCB3aXRoIHdoaWNoIGRydWcgKGR1cGlsdW1hYi9jeWNsb3Nwb3JpbmUpIHdhcyB1c2VkIGFzIHRyZWF0bWVudAoKTm8gaW5mb3JtYXRpb24gcmVnYXJkaW5nIHRoZXNlIG1pc3NpbmcgZmVhdHVyZXMgd2VyZSBmb3VuZCBpbiB0aGUgYXNzb2NpYXRlZCBwYXBlci4KCkFmdGVyIGRvd25sb2FkaW5nIHRoZSByYXcgY291bnQgbWF0cml4IGZyb20gR0VPIHZpYSBHRU9xdWVyeSBbQGdlb3F1ZXJ5XSwgdGhlIGRhdGEgd2FzIGNsZWFuZWQgKGNvbmZpcm1pbmcgdGhhdCB0aGVyZQp3ZXJlIG5vIGR1cGxpY2F0ZSBnZW5lcyksIGZpbHRlcmVkIGJ5IGdlbmUgKHJlbW92aW5nIHRob3NlIHdpdGhvdXQgYXQgbGVhc3Qgb25lIHJlYWQgcGVyIG1pbGxpb24gaW4gZWFjaCBzYW1wbGUpLCBhbmQKZmlsdGVyZWQgYnkgcGF0aWVudCAocmVtb3ZpbmcgdGhvc2Ugd2l0aCBpbmNvbXBsZXRlIGRhdGE7IHNlZSB0aGUgcG9pbnRzIGFib3ZlKS4gVGhlIGdlbmVzIChyb3dzKSB3ZXJlIGxhYmVsbGVkIGJ5CnRoZWlyIEVuc2VtYmwgZ2VuZSBJRHMsIHdoaWNoIHdlcmUgbWFwcGVkIHRvIEhVR08gZ2VuZSBzeW1ib2xzIHVzaW5nIGJpb21hUnQgW0BiaW9tYXJ0XSwgYW5kIGdlbmVzIHRoYXQgd2VyZSB1bmFibGUgdG8KYmUgY29udmVydGVkIChzdWNoIGFzIG5vdmVsIGdlbmVzKSB3ZXJlIHJlbW92ZWQuIFRvIG5vcm1hbGl6ZSB0aGUgZGF0YSwgdHJpbW1lZCBtZWFuIG9mIG0tdmFsdWVzL1RNTSB3YXMgdXNlZCBbQGVkZ2VyXSwKc2luY2Ugd2UgZm9sbG93ZWQgdGhlIGFzc3VtcHRpb24gdGhhdCBtb3N0IGdlbmVzIGFyZSBub3QgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLiBGaW5hbGx5LCB0aGUgZGF0YSB3YXMgY2hlY2tlZCBmb3IKb3V0bGllcnMgKG5vbmUgd2VyZSBmb3VuZCksIGFuZCB0aGUgcmVzdWx0aW5nIGRhdGEgZnJhbWUgd2FzIHdyaXR0ZW4gdG8gYSB0YWItZGVsaW1pdGVkIHR4dCBmaWxlIChhbmQgZ3ppcHBlZCBmb3IKdXBsb2FkIHRvIEdpdEh1YikuCgpXZSBjYW4gbG9hZCB0aGUgZGF0YXNldCBoZXJlIGFuZCB0YWtlIGEgbG9vayBhdCB0aGUgZmlyc3QgZmV3IHJvd3MgYW5kIGNvbHVtbnM6CmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBUaGUgZGF0YXNldCBzaG91bGQgb3JpZ2luYWxseSBiZSBjb21wcmVzc2VkIChHaXRIdWIgMjUgTUIpCmZpbGVwYXRoIDwtIGZpbGUucGF0aChnZXR3ZCgpLCAibm9ybWFsaXplZENvdW50RGF0YS50eHQiKQppZiAoIWZpbGUuZXhpc3RzKGZpbGVwYXRoKSkgewogIGd6ZmlsZXBhdGggPC0gZmlsZS5wYXRoKGdldHdkKCksICJub3JtYWxpemVkQ291bnREYXRhLnR4dC5neiIpCiAgaWYgKCFmaWxlLmV4aXN0cyhnemZpbGVwYXRoKSkgewogICAgc3RvcCgiRGF0YXNldCBub3QgZm91bmQiKQogIH0KICBHRU9xdWVyeTo6Z3VuemlwKGd6ZmlsZXBhdGgpCn0KCm5vcm1hbGl6ZWRDb3VudERhdGEgPC0gcmVhZC50YWJsZShmaWxlID0gZmlsZS5wYXRoKGdldHdkKCksICJub3JtYWxpemVkQ291bnREYXRhLnR4dCIpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2VwID0gIlx0IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGhlYWRlciA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjaGVjay5uYW1lcyA9IEZBTFNFKQprbml0cjo6a2FibGUobm9ybWFsaXplZENvdW50RGF0YVsxOjQsIDE6NF0sIHR5cGUgPSAiaHRtbCIpCmBgYAoKTnVtYmVyIG9mIHBhdGllbnRzOiBgciBuY29sKG5vcm1hbGl6ZWRDb3VudERhdGEpIC8gNGAgIApOdW1iZXIgb2Ygc2FtcGxlczogYHIgbmNvbChub3JtYWxpemVkQ291bnREYXRhKWAgIApOdW1iZXIgb2YgZ2VuZXM6IGByIG5yb3cobm9ybWFsaXplZENvdW50RGF0YSlgCgpUaGUgY29sdW1uIG5hbWVzIGFyZSBmb3JtYXR0ZWQgIlBhdGllbnRfXDxwYXRpZW50IG51bWJlclw+XF9cPEFOfEFMXD5fXDxtMHxtM1w+Iiwgd2hlcmUgQU4gbWVhbnMgaW50cmFwZXJzb25hbApub24tbGVzaW9uYWwsIEFMIG1lYW5zIGludHJhcGVyc29uYWwgbGVzaW9uYWwsIG0wIG1lYW5zIHRoZSBzYW1wbGUgd2FzIHRha2VuIGJlZm9yZSB0cmVhdG1lbnQsIGFuZCBtMyBtZWFucyB0aGUgc2FtcGxlCndhcyB0YWtlbiBhZnRlciB0aHJlZSBtb250aHMgb2YgdHJlYXRtZW50LiBOb3RlIHRoZSBhYnNlbmNlIG9mIHBhdGllbnQgMiBiZXR3ZWVuIHBhdGllbnRzIDEgYW5kIDMsIHdobyB3YXMgcmVtb3ZlZCBkdWUKdG8gbWlzc2luZyBkYXRhLgoKIyMgMi4gRGlmZmVyZW50aWFsIGdlbmUgZXhwcmVzc2lvbgoKVGhlIGNvcnJlc3BvbmRpbmcgam91cm5hbCBlbnRyeSBmb3IgdGhpcyBhc3NpZ25tZW50IGNhbiBiZSBmb3VuZApbaGVyZV0oaHR0cHM6Ly9naXRodWIuY29tL2JjYjQyMC0yMDIyL1lpamlhX0NoZW4vd2lraS84Li1Bc3NpZ25tZW50LTItJUUyJTgwJTkwLUFkZGl0aW9uYWwtd29yay1hbmQtZGV0YWlscykuCgpGaXJzdCwgd2Ugc2hvdWxkIGluc3BlY3QgdGhlIGRhdGEgYXMgYSBoZWF0bWFwIHRvIGdldCBhIGdlbmVyYWwgaWRlYSBvZiBnZW5lIGV4cHJlc3Npb24gbGV2ZWxzLiBCdXQgYmVmb3JlIHdlIGRvIHRoYXQsCndlIHNob3VsZCByb3ctbm9ybWFsaXplIGl0LCB3aGljaCBzY2FsZXMgcm93cyBhbmQgY2VudHJlcyB0aGVtIGFyb3VuZCB0aGUgbWVhbiwgc28gdGhhdCBubyBwYXJ0aWN1bGFyIGdlbmUgY29tcGxldGVseQpkb21pbmF0ZXMgdGhlIHBsb3QuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgUGFyYW1ldGVycyB0byBzZWxlY3QgdGhlIHJhbmdlIG9mIHNhbXBsZXMgdG8gdXNlLiBUaGUgbWF0cml4IGhhcyAxNyw3NDcgcm93cyBhbmQgOTYgY29sdW1ucywgYW5kIGlmIHRvbyBtdWNoIGRhdGEgaXMKIyBjaG9zZW4sIHRoZSBjb2RlIHdpbGwgY3Jhc2gKU0FNUExFX1NUQVJUIDwtIDEKIyBTZXR0aW5nIFNBTVBMRV9FTkQgdG8gMTIgb3IgbW9yZSBoYXMgYmVlbiB0ZXN0ZWQgdG8gY3Jhc2gKU0FNUExFX0VORCA8LSA4CmhlYXRtYXBNYXRyaXggPC0gbm9ybWFsaXplZENvdW50RGF0YVssIFNBTVBMRV9TVEFSVDpTQU1QTEVfRU5EXQoKIyBSb3ctbm9ybWFsaXphdGlvbiBjYW4gYmUgZG9uZSB3aXRoIHRoZSBiYXNlIHNjYWxlIGZ1bmN0aW9uIG9uIHRoZSB0cmFuc3Bvc2VkIG1hdHJpeApoZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwTWF0cml4KSkpCmhlYXRtYXBNYXRyaXhbaXMubmEoaGVhdG1hcE1hdHJpeCldIDwtIDAKCmlmIChtaW4oaGVhdG1hcE1hdHJpeCkgPT0gMCkgewogICMgTm8gbmVnYXRpdmUgdmFsdWVzID0+IGNvbG91cnMgc2hvdWxkIG9ubHkgcmFuZ2UgZnJvbSB3aGl0ZSB0byByZWQKICBoZWF0bWFwQ29sb3VycyA8LSBjaXJjbGl6ZTo6Y29sb3JSYW1wMihjKDAsIG1heChoZWF0bWFwTWF0cml4KSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygid2hpdGUiLCAicmVkIikpCn0gZWxzZSB7CiAgIyBIYXMgbmVnYXRpdmUgdmFsdWVzID0+IHVzZSBibHVlIGZvciB0aG9zZQogIGhlYXRtYXBDb2xvdXJzIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMobWluKGhlYXRtYXBNYXRyaXgpLCAwLCBtYXgoaGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCmN1cnJIZWF0bWFwIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGFzLm1hdHJpeChoZWF0bWFwTWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcENvbG91cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIktleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJOb3JtYWxpemVkIGhlYXRtYXAgb2Ygc2FtcGxlcyIpCgojIERpc3BsYXlpbmcgdGhlIGhlYXRtYXAgbWF5IHRha2UgYSB3aGlsZSB0byBydW4sIG9yIGp1c3QgY3Jhc2gKY3VyckhlYXRtYXAKYGBgCgpCeSBkZWZhdWx0IChhbmQgb25seSBpZiB0aGUgbGFzdCBsaW5lIGluIHRoZSBjb2RlIHNuaXBwZXQgZGlyZWN0bHkgYWJvdmUgaXMgbm90IGNvbW1lbnRlZCBvdXQpLCB0aGUgaGVhdG1hcCB3aWxsIGJlCmRpc3BsYXllZCBmb3IgZWlnaHQgc2FtcGxlcyAoZm91ciBwYXRpZW50cywgYm90aCBiZWZvcmUgdHJlYXRtZW50KS4gVGhlIHBhcmFtZXRlcnMgYXQgdGhlIHN0YXJ0IG9mIHRoZSBjb2RlIHNuaXBwZXQgY2FuCmJlIGNoYW5nZWQgdG8gZGlzcGxheSBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGVudGlyZSBoZWF0bWFwLiBSZWdhcmRsZXNzLCB3ZSBjYW4gYWxyZWFkeSBzZWUgdGhhdCB0aGVyZSBpcyBzaWduaWZpY2FudAp2YXJpYXRpb24gaW4gdGhlIGRhdGEgYmV0d2VlbiBkaWZmZXJlbnQgcGF0aWVudHMuIFRoaXMgd2lsbCBuZWVkIHRvIGJlIHRha2VuIGludG8gYWNjb3VudCB3aGVuIGRvaW5nIGZ1cnRoZXIgYW5hbHlzaXMuCgojIyMgVGVzdGluZyBhIHNpbmdsZSBnZW5lCgpUaGUgYXV0aG9ycyBvZiB0aGUgcGFwZXIgaHlwb3RoZXNpemVkIHRoYXQgSUwxMyAoaW50ZXJsZXVraW4gMTMsIGEgZ2VuZSByZWxhdGVkIHRvIHR5cGUgMiBjeXRva2luZXMgYW5kIGNoZW1va2luZXMpLAphbW9uZyBvdGhlcnMsIHdlcmUgcmVzcG9uc2libGUgZm9yIHRoZSBsZXNpb25zIGZvdW5kIGluIHBhdGllbnRzIHdpdGggQUQsIHNvIHdlIGNhbiBydW4gYSBxdWljayB0ZXN0IHRvIHNlZSBpZiB0aGVyZSBpcwphbnkgY2hhbmdlIGluIGV4cHJlc3Npb24gZm9yIHRoaXMgcGFydGljdWxhciBnZW5lIGFtb25nIHRoZSBlbnRpcmUgc2V0IG9mIHBhdGllbnRzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIEdldCB0aGUgY29sdW1ucyBjb3JyZXNwb25kaW5nIHRvIHByZS0gYW5kIHBvc3QtdHJlYXRtZW50IGFmZmVjdGVkIHNhbXBsZXMKcHJlVHJlYXRBZmZTYW1wbGVzIDwtIGdyZXAoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHBhdHRlcm4gPSAiX0FMX20wIikKcG9zdFRyZWF0QWZmU2FtcGxlcyA8LSBncmVwKGNvbG5hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpLCBwYXR0ZXJuID0gIl9BTF9tMyIpCgojIEZpbmQgdGhlIHJvdyBpbmRleCBvZiBJTDEzIGluIHRoZSBkYXRhCmlsMTNJZHggPC0gd2hpY2gocm93bmFtZXMobm9ybWFsaXplZENvdW50RGF0YSkgPT0gIklMMTMiKQoKIyBGb3JtYXQgdGhlIGV4cHJlc3Npb24gbGV2ZWxzIChmb3IgYm90aCBwcmUtIGFuZCBwb3N0LXRyZWF0bWVudCkgaW4gYSB0YWJsZSBmb3IgY29tcGFyaXNvbgppbDEzRXhwciA8LSB0KG5vcm1hbGl6ZWRDb3VudERhdGFbaWwxM0lkeCwgcHJlVHJlYXRBZmZTYW1wbGVzXSkKaWwxM0V4cHIgPC0gYXMuZGF0YS5mcmFtZShpbDEzRXhwcikKaWwxM0V4cHIgPC0gY2JpbmQoaWwxM0V4cHIsIHQobm9ybWFsaXplZENvdW50RGF0YVtpbDEzSWR4LCBwb3N0VHJlYXRBZmZTYW1wbGVzXSkpCmNvbG5hbWVzKGlsMTNFeHByKSA8LSBjKCJQcmUtdHJlYXRtZW50IiwgIlBvc3QtdHJlYXRtZW50IikKCiMgQSB2aXN1YWwgaW5zcGVjdGlvbiBvZiBzb21lIG9mIHRoZSBkYXRhIG1heSBiZSBpbnRlcmVzdGluZwprbml0cjo6a2FibGUoaWwxM0V4cHJbMTo4LCBdLCB0eXBlID0gImh0bWwiKQpgYGAKCkxvb2tpbmcgYXQgdGhlIGV4cHJlc3Npb24gY2hhbmdlIG1heSBub3QgbmVjZXNzYXJpbHkgYmUgY29uY2x1c2l2ZSwgc28gd2UgY2FuIGFsc28gdHJ5IGEgc2ltcGxlIHQtdGVzdC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KdC50ZXN0KHggPSB0KGlsMTNFeHByJGBQcmUtdHJlYXRtZW50YCksIHk9dChpbDEzRXhwciRgUG9zdC10cmVhdG1lbnRgKSkKYGBgCgpXZSBzZWUgdGhhdCB0aGUgcmVzdWx0cyBvZiB0aGUgdGVzdCBkbyBub3Qgc3VnZ2VzdCBhIGRpZmZlcmVuY2UgaW4gZXhwcmVzc2lvbiBiZWZvcmUgYW5kIGFmdGVyIHRyZWF0bWVudCBmb3IgdGhlIGVudGlyZQpzZXQgb2YgcGF0aWVudHMsIHdoaWNoIHByb3ZpZGVzIGV2aWRlbmNlIHRvIHN1Z2dlc3QgdGhhdCBwYXRpZW50IHZhcmlhdGlvbiBpcyBhIG1ham9yIGZhY3RvciB0aGF0IG5lZWRzIHRvIGJlIGFjY291bnRlZApmb3IuCgojIyMgQ3JlYXRpbmcgYSBtb2RlbAoKVG8gdHJ5IGFuZCBjb250cm9sIGZvciB0aGVzZSB2YXJpYWJsZXMsIHdlIGNhbiB1c2UgdmFyaW91cyBwYWNrYWdlcyB0aGF0IHByb3ZpZGUgbWV0aG9kcyBzcGVjaWFsaXplZCBmb3IgZGlmZmVyZW50aWFsCmV4cHJlc3Npb24gYW5hbHlzaXMuIEluIHBhcnRpY3VsYXIsIGVkZ2VSIGlzIGEgcGFja2FnZSBlc3BlY2lhbGx5IHN1aXRlZCBmb3IgYnVsayBSTkEtU2VxIGFuYWx5c2lzLCBjb250YWluaW5nIHNldmVyYWwKbGluZWFyIG1ldGhvZHMgdGhhdCB3ZSBjYW4gdXNlIFtAZWRnZXJdLiBXZSBzdGFydCBieSBzZXR0aW5nIHVwIHRoZSBjb3VudHMgaGVhZGVyIGludG8gaW5kaXZpZHVhbCBjYXRlZ29yaWVzLCB3aGljaApzaG91bGQgbWFrZSBpdCBlYXNpZXIgdG8gdXNlIGRpZmZlcmVudCBwYXJ0cyBvZiB0aGUgZGF0YSBmb3IgZGlmZmVyZW50IGFuYWx5c2VzIG9yIHBsb3RzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIERlZmluZXMgZGlmZmVyZW50IGdyb3VwcyB0aGF0IGNhbiBiZSBjb21iaW5lZCBmb3IgZGlmZmVyZW50IHRoaW5ncyAoYWRhcHRlZCBmcm9tIEExKQpzcGxpdENvbE5hbWVzIDwtIGZ1bmN0aW9uKGNvbE5hbWVzKSB7CiAgcmV0dXJuKHVubGlzdChzdHJzcGxpdChjb2xOYW1lcywgc3BsaXQgPSAiXyIpKVtjKDIsIDMsIDQpXSkKfQpncm91cHMgPC0gZGF0YS5mcmFtZShsYXBwbHkoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHNwbGl0Q29sTmFtZXMpKQpjb2xuYW1lcyhncm91cHMpIDwtIGNvbG5hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpCnJvd25hbWVzKGdyb3VwcykgPC0gYygiUGF0aWVudCIsICJTYW1wbGUgdHlwZSIsICJTYW1wbGUgdGltZSIpCmdyb3VwcyA8LSBkYXRhLmZyYW1lKHQoZ3JvdXBzKSkKCiMgQ2hlY2sgdGhlIGZpcnN0IGZldyBsaW5lcyBvZiB0aGUgd2hvbGUgdGFibGUgdG8gbWFrZSBzdXJlIHRoZSBncm91cHMgaGF2ZSBiZWVuIGRlZmluZWQgcHJvcGVybHkKa25pdHI6OmthYmxlKGdyb3Vwc1sxOjQsIF0sIHR5cGUgPSAiaHRtbCIpCmBgYAoKTmV4dCwgd2UgY2FuIHRocm93IChwYXJ0cyBvZikgdGhlIGRhdGEgaW50byBhbiBNRFMgcGxvdCBpbiBvcmRlciB0byBnZXQgc29tZSBpbnNpZ2h0IGFzIHRvIGhvdyB3ZSBzaG91bGQgc2V0IHVwIG91cgpsaW5lYXIgbW9kZWxzLgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIFBhcmFtZXRlcnMgdG8gc2VsZWN0IHdoaWNoIHN1YnNldCBvZiBkYXRhIHRvIHBsb3QKUEFUSUVOVF9TVEFSVCA8LSAxClBBVElFTlRfRU5EIDwtIDUKIyBHZXQgdGhlIGFmZmVjdGVkIHN1YnNldCBvZiB0aGUgZGF0YQphZmZlY3RlZENvdW50RGF0YSA8LSBub3JtYWxpemVkQ291bnREYXRhW2dyZXAoY29sbmFtZXMobm9ybWFsaXplZENvdW50RGF0YSksIHBhdHRlcm4gPSAiX0FMXyIpCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgXVssIGMoUEFUSUVOVF9TVEFSVDpQQVRJRU5UX0VORCwgKFBBVElFTlRfU1RBUlQgKyAyNCk6KFBBVElFTlRfRU5EICsgMjQpKV0KYWZmZWN0ZWRHcm91cHMgPC0gZ3JvdXBzW2dyb3VwcyRTYW1wbGUudHlwZSA9PSAiQUwiLAogICAgICAgICAgICAgICAgICAgICAgICAgXVtjKFBBVElFTlRfU1RBUlQ6UEFUSUVOVF9FTkQsIChQQVRJRU5UX1NUQVJUICsgMjQpOihQQVRJRU5UX0VORCArIDI0KSksIF0KCiMgQnVpbGQgdGhlIE1EUyBwbG90LCBjb2xvdXJpbmcgYnkgdGltZSAocHJlLSBhbmQgcG9zdC10cmVhdG1lbnQpCmRnZWxpc3QgPC0gZWRnZVI6OkRHRUxpc3QoY291bnRzID0gYWZmZWN0ZWRDb3VudERhdGEsIGdyb3VwID0gYWZmZWN0ZWRHcm91cHMkU2FtcGxlLnRpbWUpCmVkZ2VSOjpwbG90TURTLkRHRUxpc3QoZGdlbGlzdCwgbGFiZWxzID0gcm93bmFtZXMoYWZmZWN0ZWRHcm91cHMpLAogICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGMoImRhcmtncmVlbiIsICJibHVlIilbZmFjdG9yKGFmZmVjdGVkR3JvdXBzJFNhbXBsZS50aW1lKV0pCmxlZ2VuZCh4ID0gImJvdHRvbWxlZnQiLCBsZWdlbmQgPSBjKCJQcmUtdHJlYXRtZW50IiwgIlBvc3QtdHJlYXRtZW50IiksIGZpbGwgPSBjKCJkYXJrZ3JlZW4iLCAiYmx1ZSIpKQp0aXRsZShtYWluID0gIkRpc3RhbmNlIGJldHdlZW4gc2FtcGxlcywgY29sb3VyZWQgYnkgdHJlYXRtZW50IHRpbWUiKQpgYGAKCkFsc28gdHJ5IGdyb3VwaW5nIGJ5IHBhdGllbnQgdG8gc2VlIGFwcHJveGltYXRlbHkgaG93IG11Y2ggdmFyaWFiaWxpdHkgdGhlcmUgaXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgQ29sb3VyIGJ5IHBhdGllbnQgbnVtYmVyCnBhdGllbnRDb2xvdXJzIDwtIHJhaW5ib3coUEFUSUVOVF9FTkQgLSBQQVRJRU5UX1NUQVJUICsgMSkKZWRnZVI6OnBsb3RNRFMuREdFTGlzdChkZ2VsaXN0LCBsYWJlbHMgPSByb3duYW1lcyhhZmZlY3RlZEdyb3VwcyksCiAgICAgICAgICAgICAgICAgICAgICAgY29sID0gcGF0aWVudENvbG91cnMpCnRpdGxlKG1haW4gPSAiRGlzdGFuY2UgYmV0d2VlbiBzYW1wbGVzLCBjb2xvdXJlZCBieSBwYXRpZW50IG51bWJlciIpCmBgYAoKV2Ugc2VlIHRoYXQgdGhlcmUgaXMgbm8gZXNwZWNpYWxseSBvYnZpb3VzIGNsdXN0ZXJpbmcgaW4gdHJlYXRtZW50IHRpbWUgb3IgaW5kaXZpZHVhbCBwYXRpZW50LCB3aGljaCBjb3VsZCBiZSBleHBsYWluZWQKYnkgdGhlIGltcGVyZmVjdCBkYXRhOyBlLmcuLCBwZXJoYXBzIHRoZSBwYXRpZW50cyBzaG93biBoZXJlIHVzZWQgZGlmZmVyZW50IGRydWdzIG9yIHNvbWUgd2VyZSBhY3R1YWxseSBjb250cm9scy4KUmVnYXJkbGVzcywgdGhlcmUgaXMgaW5jZW50aXZlIHRvIHRyeSBmaXR0aW5nIHRoZSBkYXRhIHRvIGEgbGluZWFyIG1vZGVsIHRvIHNlZSBpZiBhbnkgaW1wcm92ZW1lbnRzIGNhbiBiZSBmb3VuZC4KCmVkZ2VSIHByb3ZpZGVzIGEgY29udmVuaWVudCBxdWFzaS1saWtlbGlob29kIG1ldGhvZCB0aGF0IGhhcyBiZWVuIHJlY29tbWVuZGVkIGZvciBidWxrIFJOQS1TZXEgZXhwZXJpbWVudHMgYW5kIGlzIG9mdGVuCnVzZWQgZm9yIG1vcmUgY29tcGxpY2F0ZWQgbW9kZWxzLiBTaW5jZSB0aGUgZGF0YXNldCB0aGF0IHdlIGFyZSB3b3JraW5nIHdpdGggaXMgcXVpdGUgbGFyZ2UgYW5kIGNvbXBsZXgsIGl0IG1heSBiZSBhCmdvb2QgY2FuZGlkYXRlIHRvIHVzZS4gV2Ugd2lsbCBmaXJzdCB0cnkgYSBzdHJhaWdodGZvcndhcmQgbW9kZWwgdGhhdCBkb2VzIG5vdCBhY2NvdW50IGZvciBwYXRpZW50IHZhcmlhYmlsaXR5LgoKYGBge3IsIG1lc3NhZ2UgPSBGQUxTRSwgd2FybmluZyA9IEZBTFNFfQojIENyZWF0ZSBhIGxpbmVhciBkZXNpZ24gbWF0cml4IGZvciBlZGdlUiBxdWFzaSBsaWtlbGlob29kIGluIHRyZWF0bWVudCB0aW1lCm1vZGVsRGVzaWduIDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwcyRTYW1wbGUudGltZSkKZGdlbGlzdCA8LSBlZGdlUjo6REdFTGlzdChjb3VudHMgPSBub3JtYWxpemVkQ291bnREYXRhLCBncm91cCA9IGdyb3VwcyRTYW1wbGUudGltZSkKZGdlbGlzdCA8LSBlZGdlUjo6ZXN0aW1hdGVEaXNwKGRnZWxpc3QsIG1vZGVsRGVzaWduKQptb2RlbEZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZGdlbGlzdCwgbW9kZWxEZXNpZ24pCgojIEV4dHJhY3QgdGhlIHJlc3VsdHMKcWxmVGltZSA8LSBlZGdlUjo6Z2xtUUxGVGVzdChtb2RlbEZpdCwgY29lZiA9ICJncm91cHMkU2FtcGxlLnRpbWVtMyIpCnFsZkhpdHMgPC0gZWRnZVI6OnRvcFRhZ3MocWxmVGltZSwgc29ydC5ieSA9ICJQVmFsdWUiLCBuID0gbnJvdyhub3JtYWxpemVkQ291bnREYXRhKSkKa25pdHI6OmthYmxlKGVkZ2VSOjp0b3BUYWdzKHFsZlRpbWUpWzE6OCwgXSwgdHlwZSA9ICJodG1sIikKYGBgCgpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIHRoZSB0aHJlc2hvbGQgcC12YWx1ZSAoMC4wNSk6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDUpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJEZEUiA8IDAuMDUpKWAKClNpbmNlIHdlIGFyZSBkb2luZyBtYW55IHRlc3RzLCB0aGUgcHJvYmFiaWxpdHkgdGhhdCBhIHBvc2l0aXZlIHJlc3VsdCB3aWxsIG9jY3VyIHNpbXBseSBieSBjaGFuY2UgaXMgaW5jcmVhc2VkLiBUbwpjb250cm9sIGZvciB0aGlzLCB3ZSBjYW4gdXNlIGEgbWV0aG9kIG9mIG11bHRpcGxlIGh5cG90aGVzaXMgdGVzdGluZywgd2hpY2ggd2lsbCB0cnkgdG8gY29ycmVjdCB0aGUgcG9zaXRpdml0eSByYXRlLgplZGdlUidzIHF1YXNpLWxpa2VsaWhvb2QgbWV0aG9kIGRvZXMgdGhpcyBieSB1c2luZyBmYWxzZSBkaXNjb3ZlcnkgcmF0ZSwgYSBmYWlybHkgY29tbW9uIHdheSB0byBjb3JyZWN0IHRoZSBkYXRhLgoKV2Ugc2VlIHRoYXQgYSBmYWlybHkgbGFyZ2UgbnVtYmVyIG9mIGdlbmVzIGFyZSBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQsIGV2ZW4gYWZ0ZXIgY29ycmVjdGlvbiB3aXRoIEZEUiwgc28gYW4gZXZlbgpzdHJvbmdlciB0aHJlc2hvbGQgY2FuIGJlIGNvbnNpZGVyZWQuIFRoaXMgd2lsbCBhbGxvdyBmb3IgZ3JlYXRlciBsZXZlbHMgb2YgY29uZmlkZW5jZSB3aGVuIGFuYWx5emluZyB0aGUgZGF0YSwgYW5kCndpbGwgYWxzbyBwdXQgYSBncmVhdGVyIGVtcGhhc2lzIG9uIHdoaWNoIGdlbmVzIGNhbiBiZSBjb25zaWRlcmVkIHRvIGJlIG1vcmUgaW1wb3J0YW50IGJlY2F1c2UgdGhleSBhcmUgc2lnbmlmaWNhbnRseQpkaWZmZXJlbnRpYWxseSBleHByZXNzZWQuCgpOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGEgc3RyaWN0ZXIgcC12YWx1ZSAoMC4wMSk6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDEpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzJHRhYmxlJEZEUiA8IDAuMDEpKWAKCkV2ZW4gc3RyaWN0ZXIgdGhyZXNob2xkcyBjb3VsZCBiZSBjb25zaWRlcmVkIChzdWNoIGFzIDAuMDAxKSwgYnV0IHdlIGRvIG5vdCB3YW50IHRvIGNvbXBsZXRlbHkgdGhyb3cgYXdheSB0aGUgd2Vha2VyCnNpZ25hbC4gV2UgY2FuIG5leHQgdHJ5IHRvIGZpdCB0aGUgZGF0YSB0byBhIG1vZGVsIHRoYXQgaW5jb3Jwb3JhdGVzIHBhdGllbnQgbnVtYmVyIGludG8gaXQsIHdoaWNoIG1heSBoYXZlIGJldHRlcgpyZXN1bHRzIGluIGRvd25zdHJlYW0gYW5hbHlzaXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgRXNzZW50aWFsbHkgdGhlIHNhbWUgYXMgYmVmb3JlLCBidXQgd2l0aCBhZGRpdGlvbmFsIGRhdGEKbW9kZWxEZXNpZ25QYXRpZW50IDwtIG1vZGVsLm1hdHJpeCh+IGdyb3VwcyRQYXRpZW50ICsgZ3JvdXBzJFNhbXBsZS50aW1lKQpkZ2VsaXN0IDwtIGVkZ2VSOjpER0VMaXN0KGNvdW50cyA9IG5vcm1hbGl6ZWRDb3VudERhdGEsIGdyb3VwID0gZ3JvdXBzJFNhbXBsZS50aW1lKQpkZ2VsaXN0IDwtIGVkZ2VSOjplc3RpbWF0ZURpc3AoZGdlbGlzdCwgbW9kZWxEZXNpZ25QYXRpZW50KQptb2RlbEZpdCA8LSBlZGdlUjo6Z2xtUUxGaXQoZGdlbGlzdCwgbW9kZWxEZXNpZ25QYXRpZW50KQoKcWxmVGltZSA8LSBlZGdlUjo6Z2xtUUxGVGVzdChtb2RlbEZpdCwgY29lZiA9ICJncm91cHMkU2FtcGxlLnRpbWVtMyIpCnFsZkhpdHNQYXRpZW50IDwtIGVkZ2VSOjp0b3BUYWdzKHFsZlRpbWUsIHNvcnQuYnkgPSAiUFZhbHVlIiwgbiA9IG5yb3cobm9ybWFsaXplZENvdW50RGF0YSkpCmtuaXRyOjprYWJsZShlZGdlUjo6dG9wVGFncyhxbGZUaW1lWzE6OCwgXSksIHR5cGUgPSAiaHRtbCIpCmBgYAoKTnVtYmVyIG9mIGdlbmVzIHRoYXQgcGFzcyB0aGUgdGhyZXNob2xkIHAtdmFsdWUgKDAuMDUpOiBgciBsZW5ndGgod2hpY2gocWxmSGl0c1BhdGllbnQkdGFibGUkUFZhbHVlIDwgMC4wNSkpYCAgCk51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3MgY29ycmVjdGlvbiAoRkRSKTogYHIgbGVuZ3RoKHdoaWNoKHFsZkhpdHNQYXRpZW50JHRhYmxlJEZEUiA8IDAuMDUpKWAKCk51bWJlciBvZiBnZW5lcyB0aGF0IHBhc3MgYSBzdHJpY3RlciBwLXZhbHVlICgwLjAxKTogYHIgbGVuZ3RoKHdoaWNoKHFsZkhpdHNQYXRpZW50JHRhYmxlJFBWYWx1ZSA8IDAuMDEpKWAgIApOdW1iZXIgb2YgZ2VuZXMgdGhhdCBwYXNzIGNvcnJlY3Rpb24gKEZEUik6IGByIGxlbmd0aCh3aGljaChxbGZIaXRzUGF0aWVudCR0YWJsZSRGRFIgPCAwLjAxKSlgCgpXaGVuIGFwcGx5aW5nIGEgbW9kZWwgdGhhdCB0YWtlcyBwYXRpZW50IHZhcmlhYmlsaXR5IGludG8gYWNjb3VudCwgd2Ugc2VlIHRoYXQgdGhlIG51bWJlciBvZiBnZW5lcyB0aGF0IGFyZQpzaWduaWZpY2FudGx5IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBpbmNyZWFzZXMsIHdoaWNoIGlzIG5vdCB1bmV4cGVjdGVkLgoKIyMjIEdlbmVzIG9mIGludGVyZXN0CgpBIHF1aWNrIG5leHQgc3RlcCBpcyB0byB0cnkgYW5kIGZpbmQgZ2VuZXMgb2YgaW50ZXJlc3Q7IGUuZy4sIHRob3NlIHRoYXQgYXJlIHRoZSBtb3N0IGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZC4gT25lCm1ldGhvZCBvZiBhY2NvbXBsaXNoaW5nIHRoaXMgdGFzayBpcyB0byB1c2UgYSB2b2xjYW5vIHBsb3QsIHdoaWNoIHB1dHMgcC12YWx1ZSBhZ2FpbnN0IGxvZy1mb2xkIGNoYW5nZS4gSW4gdGhpcyB3YXksCmdlbmVzIHdpdGggdGhlIG1vc3QgbWVhbmluZ2Z1bCBvciBzaWduaWZpY2FudCBjaGFuZ2VzIHdpbGwgYmUgZm91bmQgbmVhciB0aGUgdG9wLWxlZnQgYW5kIHRvcC1yaWdodCBjb3JuZXJzIG9mIHRoZQpncmFwaC4gSGVyZSwgdGhlIFIgcGFja2FnZSBFbmhhbmNlZFZvbGNhbm8gaXMgdXNlZCB0byBxdWlja2x5IHRocm93IHRvZ2V0aGVyIHN1Y2ggYSBwbG90IFtAZW5oYW5jZWR2b2xjYW5vXS4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBHZXQgdGhlIGxvZy1mb2xkIGNoYW5nZSBhbmQgcC12YWx1ZXMgZnJvbSB0aGUgcGF0aWVudC12YXJpYWJpbGl0eS1pbmNsdWRlZCBtb2RlbAp2b2xjYW5vIDwtIGRhdGEuZnJhbWUobG9nMkZDID0gcWxmVGltZSR0YWJsZSRsb2dGQywgUFZhbHVlID0gcWxmVGltZSR0YWJsZSRQVmFsdWUpCnJvd25hbWVzKHZvbGNhbm8pIDwtIHJvd25hbWVzKG5vcm1hbGl6ZWRDb3VudERhdGEpCkVuaGFuY2VkVm9sY2Fubzo6RW5oYW5jZWRWb2xjYW5vKHZvbGNhbm8sIGxhYiA9IHJvd25hbWVzKHZvbGNhbm8pLCB4ID0gImxvZzJGQyIsIHkgPSAiUFZhbHVlIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdGl0bGUgPSAiRGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIikKYGBgCgpXZSBzZWUgdGhhdCB0aGUgdmFzdCBtYWpvcml0eSBvZiBnZW5lcyAoMTcsNzQ3IGluIHRvdGFsKSBhcmUgbm90IHNpZ25pZmljYW50bHkgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkLCBiZWluZyBwbG90dGVkCnRvd2FyZCB0aGUgYm90dG9tLWNlbnRyZSBvZiB0aGUgZ3JhcGgsIGJ1dCB0aGVyZSBhcmUgYSBmZXcgZ2VuZXMgdGhhdCBwcmVzZW50IHRoZW1zZWx2ZXMgYXMgb2YgaW50ZXJlc3QuIEZvciBleGFtcGxlLAp0aGUgcGxvdCBpZGVudGlmaWVzIENDTDE4LCBJTDE5LCBOUFRYMiwgQURPUkEzLCBBTE9YMTUsIFNQQVJDLCBBQ1NNNiwgYW5kIFNQSU5LOSBhcyBnZW5lcyB3aXRoIHNpZ25pZmljYW50IGRpZmZlcmVuY2VzCmluIGxldmVscyBvZiBleHByZXNzaW9uLgoKVGhlIG5leHQgc3RlcCBpcyB0byByZXR1cm4gdG8gbG9va2luZyBhdCBoZWF0bWFwczsgd2UgY2FuIG5vdyBwbG90IHRoZSByZXN1bHRzIG9mIGFwcGx5aW5nIHRoZSBxdWFzaS1saWtlbGlob29kIG1vZGVsCnRvIHRoZSBkYXRhLCB3aGljaCBtYXkgcmVzdWx0IGluIGNsZWFyZXIgY2x1c3RlcnMgdGhhdCBjYW4gYmUgdmlzdWFsbHkgaWRlbnRpZmllZC4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBVc2luZyB0aGUgc3RyaWN0ZXIgcC12YWx1ZSB0aHJlc2hvbGQKdG9wSGl0cyA8LSByb3duYW1lcyhxbGZIaXRzJHRhYmxlKVtxbGZIaXRzJHRhYmxlJFBWYWx1ZSA8IDAuMDFdCiMgV2FybmluZzogaGVhdG1hcE1hdHJpeCBtYXkgb25seSBoYXZlIGJlZW4gYnVpbHQgd2l0aCBhIHN1YnNldCBvZiB0aGUgZGF0YS4gdG9wSGVhdG1hcE1hdHJpeCBoZXJlIHdpbGwgcmVmbGVjdCB0aGF0CiMgc3Vic2V0LiBDaGFuZ2UgU0FNUExFX1NUQVJUIGFuZCBTQU1QTEVfRU5EIGluIHRoZSBjb2RlIHNuaXBwZXQgYXQgdGhlIHN0YXJ0IG9mIHRoaXMgc2VjdGlvbiB0byBjaGFuZ2UgdGhlIHNlbGVjdGVkCiMgZGF0YQp0b3BIZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChoZWF0bWFwTWF0cml4W3doaWNoKHJvd25hbWVzKGhlYXRtYXBNYXRyaXgpICVpbiUgdG9wSGl0cyksIF0pKSkKdG9wSGVhdG1hcE1hdHJpeFtpcy5uYSh0b3BIZWF0bWFwTWF0cml4KV0gPC0gMAoKaWYgKG1pbih0b3BIZWF0bWFwTWF0cml4KSA9PSAwKSB7CiAgaGVhdG1hcENvbG91cnMgPC0gY2lyY2xpemU6OmNvbG9yUmFtcDIoYygwLCBtYXgodG9wSGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoIndoaXRlIiwgInJlZCIpKQp9IGVsc2UgewogIGhlYXRtYXBDb2xvdXJzIDwtIGNpcmNsaXplOjpjb2xvclJhbXAyKGMobWluKHRvcEhlYXRtYXBNYXRyaXgpLCAwLCBtYXgodG9wSGVhdG1hcE1hdHJpeCkpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGMoImJsdWUiLCAid2hpdGUiLCAicmVkIikpCn0KCmdyb3VwQ29sb3VycyA8LSBjKCJibHVlIiwgInJlZCIpCm5hbWVzKGdyb3VwQ29sb3VycykgPC0gdW5pcXVlKGdyb3VwcyRTYW1wbGUudHlwZSkKaGVhdG1hcEFubm8gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShUeXBlID0gZ3JvdXBzJFNhbXBsZS50eXBlW1NBTVBMRV9TVEFSVDpTQU1QTEVfRU5EXSksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBsaXN0KHR5cGUgPSBncm91cENvbG91cnMpKQoKY3VyckhlYXRtYXAgPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXAoYXMubWF0cml4KHRvcEhlYXRtYXBNYXRyaXgpLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGhlYXRtYXBDb2xvdXJzLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9uYW1lcyA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X25hbWVzID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfaGVhdG1hcF9sZWdlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjbHVzdGVyX2NvbHVtbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmFtZSA9ICJLZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2x1bW5fdGl0bGUgPSAiVG9wIGhpdHMgaGVhdG1hcCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRvcF9hbm5vdGF0aW9uID0gaGVhdG1hcEFubm8pCgpjdXJySGVhdG1hcApgYGAKClRoaXMgaGVhdG1hcCBpcyBub3QgcGFydGljdWxhcmx5IHVzZWZ1bCBpbiBkZXRlcm1pbmluZyBpZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgY2x1c3RlciwgYmVjYXVzZSB0aGUgY29sdW1ucwphcmUgb3JkZXJlZCBzdWNoIHRoYXQgaW5kaXZpZHVhbCBwYXRpZW50IGRhdGEgaXMgcGFpcmVkIHRvZ2V0aGVyLiBGb3IgZXhhbXBsZSwgdGhlIGZpcnN0IGNvbHVtbiBoZXJlIGlzIHRoZSBjb250cm9sCnNhbXBsZSBmb3IgdGhlIGZpcnN0IHBhdGllbnQgKHRoZSBub24tYWZmZWN0ZWQgc2tpbikgcHJlLXRyZWF0bWVudCwgYW5kIHRoZSBzZWNvbmQgY29sdW1uIGlzIHRoZSBhZmZlY3RlZCBza2luIHNhbXBsZSwKYWxzbyBwcmUtdHJlYXRtZW50LiBJdCBzaG91bGQgYmUgcG9zc2libGUgdG8gZ2V0IGEgYmV0dGVyIGlkZWEgb2YgY2x1c3RlcmluZyBvdmVyIHRpbWUgYnkgcmVvcmRlcmluZyB0aGUgY29sdW1ucy4KCmBgYHtyLCBtZXNzYWdlID0gRkFMU0UsIHdhcm5pbmcgPSBGQUxTRX0KIyBCdWlsZCBhIG5ldyBoZWF0bWFwIG1hdHJpeCB0byBkZWNvdXBsZSBmcm9tIFNBTVBMRV9TVEFSVCBhbmQgU0FNUExFX0VORApyZUhlYXRtYXBNYXRyaXggPC0gbm9ybWFsaXplZENvdW50RGF0YQpyZUhlYXRtYXBNYXRyaXggPC0gdChzY2FsZSh0KHJlSGVhdG1hcE1hdHJpeCkpKQpyZUhlYXRtYXBNYXRyaXhbaXMubmEocmVIZWF0bWFwTWF0cml4KV0gPC0gMAoKIyBEb24ndCBrbm93IGhvdyB0byBkbyB0aGlzIGluIGEgb25lLWxpbmVyCm5ld0lkeHMgPC0gaW50ZWdlcihTQU1QTEVfRU5EIC0gU0FNUExFX1NUQVJUICsgMSkKY3VycklkeCA8LSAxCiMgQWRkIHRoZSAqX0FOX20wIGZpcnN0CmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJULCBTQU1QTEVfRU5ELCAyKSkgewogIG5ld0lkeHNbY3VycklkeF0gPC0gaQogIGN1cnJJZHggPC0gY3VycklkeCArIDEKfQojIFRoZW4gYWRkIHRoZSAqX0FOX20zCmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJULCBTQU1QTEVfRU5ELCAyKSkgewogIG5ld0lkeHNbY3VycklkeF0gPC0gaSArIDQ4CiAgY3VycklkeCA8LSBjdXJySWR4ICsgMQp9Cgp0b3BIZWF0bWFwTWF0cml4IDwtIHQoc2NhbGUodChyZUhlYXRtYXBNYXRyaXhbd2hpY2gocm93bmFtZXMocmVIZWF0bWFwTWF0cml4KSAlaW4lIHRvcEhpdHMpLCBdKSkpCnRvcEhlYXRtYXBNYXRyaXhbaXMubmEodG9wSGVhdG1hcE1hdHJpeCldIDwtIDAKdG9wSGVhdG1hcE1hdHJpeCA8LSB0b3BIZWF0bWFwTWF0cml4WywgbmV3SWR4c10KCmdyb3VwQ29sb3VycyA8LSBjKCJibHVlIiwgInJlZCIpCm5hbWVzKGdyb3VwQ29sb3VycykgPC0gdW5pcXVlKGdyb3VwcyRTYW1wbGUudGltZSkKaGVhdG1hcEFubm8gPC0gQ29tcGxleEhlYXRtYXA6OkhlYXRtYXBBbm5vdGF0aW9uKGRmID0gZGF0YS5mcmFtZShUaW1lID0gZ3JvdXBzJFNhbXBsZS50aW1lW25ld0lkeHNdKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbCA9IGxpc3QodHlwZSA9IGdyb3VwQ29sb3VycykpCgpjdXJySGVhdG1hcCA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcChhcy5tYXRyaXgodG9wSGVhdG1hcE1hdHJpeCksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfcm93X2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2NvbHVtbl9kZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gaGVhdG1hcENvbG91cnMsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX25hbWVzID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfbmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19oZWF0bWFwX2xlZ2VuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNsdXN0ZXJfY29sdW1ucyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYW1lID0gIktleSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNvbHVtbl90aXRsZSA9ICJUb3AgaGl0cywgY29udHJvbCwgb3ZlciB0aW1lIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgdG9wX2Fubm90YXRpb24gPSBoZWF0bWFwQW5ubykKCmN1cnJIZWF0bWFwCmBgYAoKVGhlIGFib3ZlIGhlYXRtYXAgaGFzIGJlZW4gcmVhcnJhbmdlZCBzdWNoIHRoYXQgYWxsIGVpZ2h0IGNvbHVtbnMgYXJlIGNvbnRyb2wgKG5vbi1hZmZlY3RlZC9ub24tbGVzaW9uYWwpIHNraW4gb2YKYWZmZWN0ZWQgcGF0aWVudHMsIHdpdGggdGhlIGxlZnQgZm91ciBjb2x1bW5zIGJlaW5nIHNhbXBsZXMgdGFrZW4ganVzdCBwcmlvciB0byB0cmVhdG1lbnQsIGFuZCB0aGUgcmlnaHQgZm91ciBzYW1wbGVzCmJlaW5nIHRha2VuIGFmdGVyIHRocmVlIG1vbnRocyBvZiB0cmVhdG1lbnQuIFNpbmNlIHRoaXMgc3Vic2V0IG9mIHRoZSBkYXRhc2V0IGRvZXMgbm90IGNvbnRhaW4gYW55IHNhbXBsZXMgb2YgYWZmZWN0ZWQKKGxlc2lvbmFsKSBza2luLCBpdCBpcyBub3Qgc3VycHJpc2luZyB0aGF0IGxldmVscyBvZiBnZW5lIGV4cHJlc3Npb24gYXJlIHNpbWlsYXIgYWNyb3NzIHRoZSB0d28gY2F0ZWdvcmllcy4gVGhlIG1vc3QKb2J2aW91cyBkaWZmZXJlbmNlIHdvdWxkIGJlIHdpdGggcmVzcGVjdCB0byBwYXRpZW50IDMgKGFuZCBwYXRpZW50IDEsIHRvIGEgY2VydGFpbiBleHRlbnQpLCB3aGVyZSB0aGUgZm9sbG93LXVwIHNhbXBsZQpoYXMgZ3JlYXRlciBsZXZlbHMgb2YgcmVndWxhdGlvbi0tYnV0IGl0IGlzIHN0aWxsIG9mIHRoZSBzYW1lIHR5cGUuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgRG9pbmcgdGhlIHNhbWUgYXMgYWJvdmUgZXhjZXB0IHdpdGggKl9BTF8qIGluc3RlYWQgb2YgKl9BTl8qCm5ld0lkeHMgPC0gaW50ZWdlcihTQU1QTEVfRU5EIC0gU0FNUExFX1NUQVJUICsgMSkKY3VycklkeCA8LSAxCmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJUICsgMSwgU0FNUExFX0VORCArIDEsIDIpKSB7CiAgbmV3SWR4c1tjdXJySWR4XSA8LSBpCiAgY3VycklkeCA8LSBjdXJySWR4ICsgMQp9CmZvciAoaSBpbiBzZXEoU0FNUExFX1NUQVJUICsgMSwgU0FNUExFX0VORCArIDEsIDIpKSB7CiAgbmV3SWR4c1tjdXJySWR4XSA8LSBpICsgNDgKICBjdXJySWR4IDwtIGN1cnJJZHggKyAxCn0KCnRvcEhlYXRtYXBNYXRyaXggPC0gdChzY2FsZSh0KHJlSGVhdG1hcE1hdHJpeFt3aGljaChyb3duYW1lcyhyZUhlYXRtYXBNYXRyaXgpICVpbiUgdG9wSGl0cyksIF0pKSkKdG9wSGVhdG1hcE1hdHJpeFtpcy5uYSh0b3BIZWF0bWFwTWF0cml4KV0gPC0gMAp0b3BIZWF0bWFwTWF0cml4IDwtIHRvcEhlYXRtYXBNYXRyaXhbLCBuZXdJZHhzXQoKZ3JvdXBDb2xvdXJzIDwtIGMoImJsdWUiLCAicmVkIikKbmFtZXMoZ3JvdXBDb2xvdXJzKSA8LSB1bmlxdWUoZ3JvdXBzJFNhbXBsZS50aW1lKQpoZWF0bWFwQW5ubyA8LSBDb21wbGV4SGVhdG1hcDo6SGVhdG1hcEFubm90YXRpb24oZGYgPSBkYXRhLmZyYW1lKFRpbWUgPSBncm91cHMkU2FtcGxlLnRpbWVbbmV3SWR4c10pLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sID0gbGlzdCh0eXBlID0gZ3JvdXBDb2xvdXJzKSkKCmN1cnJIZWF0bWFwIDwtIENvbXBsZXhIZWF0bWFwOjpIZWF0bWFwKGFzLm1hdHJpeCh0b3BIZWF0bWFwTWF0cml4KSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19yb3dfZGVuZCA9IFRSVUUsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNob3dfY29sdW1uX2RlbmQgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBjb2wgPSBoZWF0bWFwQ29sb3VycywKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgc2hvd19jb2x1bW5fbmFtZXMgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X3Jvd19uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBzaG93X2hlYXRtYXBfbGVnZW5kID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY2x1c3Rlcl9jb2x1bW5zID0gRkFMU0UsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIG5hbWUgPSAiS2V5IiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sdW1uX3RpdGxlID0gIlRvcCBoaXRzLCBvdmVyIHRpbWUiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICB0b3BfYW5ub3RhdGlvbiA9IGhlYXRtYXBBbm5vKQoKY3VyckhlYXRtYXAKYGBgCgpXaGVuIHdlIGxvb2sgYXQgdGhlIGhlYXRtYXAgY29uY2VybmluZyBhZmZlY3RlZCBza2luLCB0aGUgZGlmZmVyZW50aWFsIGV4cHJlc3Npb24gaXMgbW9yZSBvYnZpb3VzIGFuZCBtb3JlIHNpZ25pZmljYW50LgpUaGVyZSBhcmUgbWFueSBjbHVzdGVycyBvZiBnZW5lcyBvbiB0aGUgcmlnaHQgc2lkZSBvZiB0aGUgcGxvdCB0aGF0IGNvcnJlc3BvbmQgaW52ZXJzZWx5IHRvIHRoZSBzYW1lIHJlZ2lvbiBvbiB0aGUgbGVmdApzaWRlIG9mIHRoZSBwbG90LiBGb3IgZXhhbXBsZSwgdGhlIGNvbHVtbiBvZiBwYXRpZW50IDcgaGFzIGEgbGFyZ2UsIHN0cm9uZ2x5IGV4cHJlc3NlZCBncm91cCBvZiBnZW5lcyBuZWFyIHRoZSBtaWRkbGUKb2YgdGhlIGNvbHVtbi4gSG93ZXZlciwgd2hlbiBsb29raW5nIGF0IHRoZSBzYW1lIGFyZWEgb24gdGhlIGxlZnQgc2lkZSBvZiB0aGUgaGVhdG1hcCwgdGhlIGV4cHJlc3Npb24gb2YgdGhlIHNhbWUgZ2VuZXMKaXMgdmVyeSB3ZWFrIGluIGNvbXBhcmlzb24uIFRodXMsIHdlIGNhbiBoeXBvdGhlc2l6ZSB0aGF0IHBlcmhhcHMgdGhlc2UgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIG1heSBiZQpyZXNwb25zaWJsZSBmb3IgQUQgYW5kL29yIGl0cyBzeW1wdG9tcy4KCiMjIDMuIFRocmVzaG9sZGVkIG92ZXItcmVwcmVzZW50YXRpb24gYW5hbHlzaXMKCkluIHRoaXMgbmV4dCBwYXJ0IG9mIHRoZSBub3RlYm9vaywgdGhlIHNldHMgb2Ygc2lnbmlmaWNhbnRseSB1cC1yZWd1bGF0ZWQgYW5kIGRvd24tcmVndWxhdGVkIGdlbmVzIHdpbGwgYmUgcnVuIHRocm91Z2gKYSB0aHJlc2hvbGRlZCBnZW5lIHNldCBlbnJpY2htZW50IGFuYWx5c2lzLiBUaGUgZmlyc3Qgc3RlcCBpcyB0byBnZXQgdGhlIGxpc3RzIG9mIHN1Y2ggZ2VuZXMuCgpgYGB7ciwgbWVzc2FnZSA9IEZBTFNFLCB3YXJuaW5nID0gRkFMU0V9CiMgQ29uc3RydWN0IHRoZSByYW5rZWQgZ2VuZSBsaXN0IChub24tdGhyZXNob2xkZWQpCnFsZkhpdHNUIDwtIGFzLmRhdGEuZnJhbWUocWxmSGl0c1BhdGllbnQpCnFsZkhpdHNUWyJyYW5rIl0gPC0gLWxvZyhxbGZIaXRzVCRQVmFsdWUsIGJhc2UgPSAxMCkgKiBzaWduKHFsZkhpdHNUJGxvZ0ZDKQpxbGZIaXRzVCA8LSBxbGZIaXRzVFtvcmRlcihxbGZIaXRzVCRyYW5rKSwgXQoKd3JpdGUudGFibGUoZGF0YS5mcmFtZShnZW5lbmFtZSA9IHJvd25hbWVzKHFsZkhpdHNUKSwgRnN0YXQgPSBxbGZIaXRzVCRyYW5rKSwKICAgICAgICAgICAgZmlsZSA9IGZpbGUucGF0aChnZXR3ZCgpLCAicmFua2VkR2VuZWxpc3QudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCiMgRm9yIHRoZSB0aHJlc2hvbGRlZCBsaXN0cywgdXNlIHAgPSAwLjA1IGluc3RlYWQgb2YgMC4wMSB0byBnZXQgbW9yZSByZXN1bHRzCnVwcmVnR2VuZXMgPC0gcm93bmFtZXMocWxmSGl0c1Rbd2hpY2gocWxmSGl0c1QkUFZhbHVlIDwgMC4wNSAmIHFsZkhpdHNUJGxvZ0ZDID4gMCksIF0pCndyaXRlLnRhYmxlKHVwcmVnR2VuZXMsCiAgICAgICAgICAgIGZpbGUgPSBmaWxlLnBhdGgoZ2V0d2QoKSwgInVwcmVndWxhdGVkR2VuZXMudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKCmRvd25yZWdHZW5lcyA8LSByb3duYW1lcyhxbGZIaXRzVFt3aGljaChxbGZIaXRzVCRQVmFsdWUgPCAwLjA1ICYgcWxmSGl0c1QkbG9nRkMgPCAwKSwgXSkKd3JpdGUudGFibGUoZG93bnJlZ0dlbmVzLAogICAgICAgICAgICBmaWxlID0gZmlsZS5wYXRoKGdldHdkKCksICJkb3ducmVndWxhdGVkR2VuZXMudHh0IiksCiAgICAgICAgICAgIHNlcCA9ICJcdCIsCiAgICAgICAgICAgIHJvdy5uYW1lcyA9IEZBTFNFLAogICAgICAgICAgICBjb2wubmFtZXMgPSBGQUxTRSwKICAgICAgICAgICAgcXVvdGUgPSBGQUxTRSkKYGBgCgpUaGUgdGVjaG5pcXVlIHRoYXQgaGFzIGJlZW4gY2hvc2VuIHRvIHBlcmZvcm0gdGhpcyBhbmFseXNpcyBpcyBGaXNoZXIncyBleGFjdCB0ZXN0LCBkb25lIHRocm91Z2ggZzpQcm9maWxlcgpbQGdwcm9maWxlcl0uIEZpc2hlcidzIGV4YWN0IHRlc3QgaXMgYSBjb21tb24gc3RhdGlzdGljYWwgbWV0aG9kIHRvIGNoZWNrIGZvciBvdmVycmVwcmVzZW50YXRpb24sIHdoaWNoIGlzIHRoZSBnb2FsIG9mCnRoaXMgYW5hbHlzaXMsIGFuZCBpcyBhYmxlIHRvIGNhbGN1bGF0ZSB0aGUgZGV2aWF0aW9uIGZyb20gcC12YWx1ZXMgZXhhY3RseS4gZzpQcm9maWxlciBpcyBhIGNvbW1vbiwgY29udmVuaWVudAooYWNjZXNzaWJsZSBvbmxpbmUgYW5kIHRocm91Z2ggYW4gUiBwYWNrYWdlKSBpbXBsZW1lbnRhdGlvbiBvZiBGaXNoZXIncyBleGFjdCB0ZXN0IHRoYXQgaXMgdXBkYXRlZCBmYWlybHkgcmVndWxhcmx5LAp3aGljaCBjYW4gaGVscCBpbmNyZWFzZSB0aGUgYWNjdXJhY3kgb2YgdGhlIGFuYWx5c2lzLiBGb3Igb3VyIHB1cnBvc2UsIGFuZCBpbiB0aGUgaW50ZXJlc3Qgb2YgdGltZSwgZzpQcm9maWxlciB3aWxsIGJlCmFjY2Vzc2VkIHRocm91Z2ggaXRzIHdlYnNpdGUgKGluc3RlYWQgb2YgdXNpbmcgdGhlIFIgcGFja2FnZSkuCgpUaGUgYW5ub3RhdGlvbiBkYXRhc2V0cyB0aGF0IGhhdmUgYmVlbiBzZWxlY3RlZCBpbmNsdWRlIEdPIGJpb2xvZ2ljYWwgcHJvY2Vzc2VzICh1cGRhdGVkIDIwMjEtMTItMTUpIC0gd2l0aG91dAplbGVjdHJvbmljIGFubm90YXRpb25zLCBSZWFjdG9tZSAodXBkYXRlZCAyMDIyLTAxLTAzKSwgYW5kIFdpa2lQYXRod2F5cyAodXBkYXRlZCAyMDIxLTEyLTEwKS4gVGhlc2UgdGhyZWUgZGF0YXNldHMgYXJlCm1ham9yIGFubm90YXRpb24gcmVzb3VyY2VzLCBhbmQgaGF2ZSBiZWVuIHVwZGF0ZWQgZmFpcmx5IHJlY2VudGx5LCBzbyBpdCBpcyByZWFzb25hYmxlIHRvIGV4cGVjdCBhY2N1cmFjdGUgcmVzdWx0cy4KT3RoZXIgbWFqb3IgcmVzb3VyY2VzIHN1Y2ggYXMgS0VHRyBoYXZlIG5vdCBiZWVuIGluY2x1ZGVkIGJlY2F1c2UgdGhleSBtYXkgYWRkIGFkZGl0aW9uYWwgZXh0cmFuZW91cyBpbmZvcm1hdGlvbiB0byB0aGUKcmVzdWx0cywgYW5kIHdpdGggdGhlc2UgdGhyZWUgZGF0YXNldHMsIHRoZXJlIHNob3VsZCBhbHJlYWR5IGJlIGVub3VnaCBpbmZvcm1hdGlvbiBmb3IgYSB0aG9yb3VnaCBhbmFseXNpcy4KCldlIHdpbGwgc3RhcnQgYnkgbG9va2luZyBhdCBzdWJzZXRzIG9mIHRoZSBnZW5lcyBvZiBpbnRlcmVzdCAoaS5lLiwgdGhvc2Ugd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbiksCmFuZCB0aGVuIGZvbGxvdyB1cCBieSBhbmFseXppbmcgdGhlIGVudGlyZSBzZXQgdG8gY29tcGFyZSBob3cgbG9va2luZyBhdCBkaWZmZXJlbnQgcGFydHMgb2YgdGhlIGRhdGEgbWF5IGFmZmVjdCB0aGUKcmVzdWx0cy4gRm9yIGVhY2ggcnVuLCBGRFIgd2lsbCBiZSB1c2VkIGZvciBtdWx0aXBsZSBoeXBvdGhlc2lzIGNvcnJlY3Rpb24sIHdpdGggYSB0aHJlc2hvbGQgcC12YWx1ZSBvZiAwLjA1LiBUaGlzIHdhcwpjaG9zZW4gaW5zdGVhZCBvZiB0aGUgbW9yZSBzdHJpbmdlbnQgMC4wMSB0byBhbGxvdyBmb3IgbW9yZSBpbmZvcm1hdGlvbiBhbmQgYSBicm9hZGVyIGxvb2sgYXQgdGhlIGFzc29jaWF0ZWQgcGF0aHdheXMuCkluIGFkZGl0aW9uLCB0aGUgcmVzdWx0aW5nIHBhdGh3YXlzIHdlcmUgZmlsdGVyZWQgdG8gb25seSBpbmNsdWRlIHRob3NlIHdpdGggZ2VuZSBzZXQgc2l6ZXMgb2YgMjAwIG9yIGxlc3MuIFRoaXMgaXMKYmVjYXVzZSBvZiB0aGUgaW5oZXJpdGFuY2UtbGlrZSBwcm9wZXJ0eSBvZiB0aGUgYW5ub3RhdGlvbiByZXNvdXJjZXMsIHdoaWNoIG1heSBjbHV0dGVyIHRoZSByZXN1bHRzIGJ5IGluY2x1ZGluZwpwYXRod2F5cyB0aGF0IGFyZSB0b28gZ2VuZXJpYy4KCiMjIyBVcHJlZ3VsYXRlZCBnZW5lcwoKVGhlIGZpcnN0IHN1YnNldCBpcyBhbGwgdXByZWd1bGF0ZWQgZ2VuZXMgd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gVGhlIGxpc3Qgb2YgdXByZWd1bGF0ZWQgZ2VuZXMgKGZyb20KdGhlIGNvZGUgc25pcHBldCBhYm92ZSkgd2FzIGNvcGllZCB0byBnOlByb2ZpbGVyLCB0aGUgbWVudGlvbmVkIG9wdGlvbnMgd2VyZSBzZXQgKHdpdGggYWxsIHJlc3VsdHMgY2hlY2tlZCksIGFuZCB0aGUKYW5hbHlzaXMgd2FzIHJ1bi4gVGhlIHJlc3VsdCB3YXMgdGhlIGZvbGxvd2luZyBmaWd1cmU6CgohW0ZpZ3VyZSAxLiB1cHJlZ3VsYXRlZCBnZW5lcyBvbmx5XShmaWd1cmVzL2dwcm9mX3VwLnBuZykKCmc6UHJvZmlsZXIgaGFzIG9taXR0ZWQgdGl0bGVzIGZvciB0aGUgcGxvdCBhbmQgdGFibGUsIGJ1dCBhcHByb3ByaWF0ZSBvbmVzIHdvdWxkIGJlICJPdmVyLXJlcHJlc2VudGF0aW9uIG9mIHVwcmVndWxhdGVkCmdlbmVzIHdpdGggcC12YWx1ZSA8IDAuMDUiIGFuZCAiSGlnaGVzdCBzY29yaW5nIHBhdGh3YXlzIGZvciBnZW5lIHNldHMgd2l0aCBzaXplIGxlc3MgdGhhbiAyMDAiLCByZXNwZWN0aXZlbHkuCgpVc2luZyB0aGUgdGhyZXNob2xkcyBtZW50aW9uZWQgcHJldmlvdXNseSwgR086QlAgZm91bmQgNSwwMjcgcGF0aHdheXMsIFJFQUMgZm91bmQgMSwwMDcsIGFuZCBXUCBmb3VuZCA0NDUuIFRoZSBwbG90IGF0CnRoZSB0b3Agb2YgdGhlIGZpZ3VyZSBpcyBhIHZpc3VhbCByZXByZXNlbnRhdGlvbiBvZiBhZGp1c3RlZCBwLXZhbHVlcyB2cy4gcmVzb3VyY2UsIHdoZXJlIGVhY2ggZG90IHJlcHJlc2VudHMgYQpzcGVjaWZpYyBwYXRod2F5IChlYWNoIGFubm90YXRpb24gcmVzb3VyY2UgaGFzIGl0cyBvd24gY29sb3VyKS4gVGhlIHRhYmxlIGF0IHRoZSBib3R0b20gb2YgdGhlIGZpZ3VyZSBpcyBhIGxpc3Qgb2YgdGhlCnRvcCAoYXBwcm94aW1hdGVseSAxNSkgcGF0aHdheXMsIGFjY29yZGluZyB0byBhZGp1c3RlZCBwLXZhbHVlLCBmb3IgZWFjaCByZXNvdXJjZS4gVGhlIGluZGl2aWR1YWwgcGF0aHdheXMgYXJlIGdpdmVuCm51bWJlcnMsIGFuZCB0aGVzZSBjb3JyZXNwb25kIHRvIHRoZSBsYWJlbGxpbmcgb2YgdGhlIGRvdHMgaW4gdGhlIHBsb3QgYWJvdmUuIFdlIHNlZSB0aGF0IGRlc3BpdGUgYmVpbmcgdGhlIHBhdGh3YXlzCndpdGggdGhlIGdyZWF0ZXN0IGFkanVzdGVkIHAtdmFsdWUsIHRoZXNlIGxhYmVsbGVkIGRvdHMgYXJlIG5vdCB0aGUgaGlnaGVzdCBvbiB0aGUgcGxvdCBiZWNhdXNlIG9mIHRoZSB0aHJlc2hvbGRzIHRoYXQKd2VyZSB1c2VkOyB0aGUgdW5sYWJlbGxlZCBkb3RzIHdvdWxkIHJlcHJlc2VudCBtb3JlIGdlbmVyaWMgcGF0aHdheXMuCgpUaGUgc3BlY2lmaWMgcGF0aHdheXMgdGhhdCB3ZXJlIGZvdW5kIGhlcmUtLWZvciBleGFtcGxlLCBjb2xsYWdlbiBmaWJyaWwgb3JnYW5pemF0aW9uIGFuZCBzZXZlcmFsIGZhdHR5IGFjaWQgbWV0YWJvbGljCnByb2Nlc3Nlcy0tYXJlIG5vdCBvYnZpb3VzbHkgaW5kaWNhdGl2ZSBvZiBBRCwgYSBza2luIGRpc2Vhc2Ugb2YgYXV0b2ltbXVuZSBvcmlnaW4uCgojIyMgRG93bnJlZ3VsYXRlZCBnZW5lcwoKVGhlIHNlY29uZCBzdWJzZXQgaXMgYWxsIGRvd25yZWd1bGF0ZWQgZ2VuZXMgd2l0aCBzaWduaWZpY2FudCBkaWZmZXJlbnRpYWwgZXhwcmVzc2lvbi4gVGhlIGxpc3Qgb2YgZG93bnJlZ3VsYXRlZCBnZW5lcwp3YXMgY29waWVkIHRvIGc6UHJvZmlsZXIsIG9wdGlvbnMgd2VyZSBzZXQgKHRoaXMgdGltZSB3aXRob3V0IGFsbCByZXN1bHRzLCBzaW5jZSB0aGUgbGlzdCBvZiBnZW5lcyB3YXMgdG9vIGxhcmdlKSwgYW5kCnRoZSBhbmFseXNpcyB3YXMgcnVuLiBUaGUgcmVzdWx0IHdhcyB0aGUgZm9sbG93aW5nIGZpZ3VyZToKCiFbRmlndXJlIDIuIGRvd25yZWd1bGF0ZWQgZ2VuZXMgb25seV0oZmlndXJlcy9ncHJvZl9kb3duLnBuZykKCmc6UHJvZmlsZXIgaGFzIG9taXR0ZWQgdGl0bGVzIGZvciB0aGUgcGxvdCBhbmQgdGFibGUsIGJ1dCBhcHByb3ByaWF0ZSBvbmVzIHdvdWxkIGJlICJPdmVyLXJlcHJlc2VudGF0aW9uIG9mCmRvd25yZWd1bGF0ZWQgZ2VuZXMgd2l0aCBwLXZhbHVlIDwgMC4wNSIgYW5kICJIaWdoZXN0IHNjb3JpbmcgcGF0aHdheXMgZm9yIGdlbmUgc2V0cyB3aXRoIHNpemUgbGVzcyB0aGFuIDIwMCIsCnJlc3BlY3RpdmVseS4KClVzaW5nIHRoZSB0aHJlc2hvbGRzIG1lbnRpb25lZCBwcmV2aW91c2x5LCBHTzpCUCBmb3VuZCAyMzAgcGF0aHdheXMsIFJFQUMgZm91bmQgNzIsIGFuZCBXUCBmb3VuZCA1Mi4gVGhlIHNwZWNpZmljCnBhdGh3YXlzIHRoYXQgd2VyZSBmb3VuZCBoZXJlLS1mb3IgZXhhbXBsZSwgdmFyaW91cyBETkEgcmVwbGljYXRpb24gcGF0aHdheXMgYW5kIGNlbGwgY3ljbGUgc2lnbmFsbGluZy0taXMsIGxpa2UgdGhlCnVwcmVndWxhdGVkIGdlbmVzLCBub3QgZGlyZWN0bHkgcmVsYXRlZCB0byBBRC4gSG93ZXZlciwgdGhlc2UgZXhhbXBsZXMgYXJlIG5vdCBhcyBicm9hZCwgYW5kIGl0IGlzIHBvc3NpYmxlIHRoYXQgdGhlcmUKaXMgYW4gaW5kaXJlY3QgbGluay4gRm9yIGV4YW1wbGUsIHBzb3JpYXNpcywgYW5vdGhlciBza2luIGNvbmRpdGlvbiBzaW1pbGFyIHRvIGF0b3BpYyBkZXJtYXRpdGlzLCBpcyBkaXJlY3RseSByZWxhdGVkCnRvIHRoZSBkaXNyZWd1bGF0aW9uIG9mIHRoZSBjZWxsIGN5Y2xlIG9mIHNraW4gY2VsbHMsIHNvIGl0IHdvdWxkIG5vdCBiZSBhIHN1cnByaXNlIHRvIHNlZSBhIHNpbWlsYXIgcmVzdWx0IGhlcmUuCgojIyMgQWxsIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcwoKSXQgaXMgaW1wb3J0YW50IHRvIGNvbnNpZGVyIHRoZSBlbnRpcmUgc2V0IG9mIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhcyBhIHdob2xlLCBiZWNhdXNlIGl0IGlzIHBvc3NpYmxlIHRoYXQKdGhlIHBhcnRpY3VsYXIgcGF0aHdheXMgdGhhdCB3ZSBhcmUgZXhwZWN0aW5nIGhhdmUgYm90aCB1cHJlZ3VsYXRlZCBhbmQgZG93bnJlZ3VsYXRlZCBnZW5lcy4gVGhlIHJlc3VsdCBvZiBydW5uaW5nCmc6UHJvZmlsZXIgb24gdGhlIGxpc3Qgb2YgY29tYmluZWQgZ2VuZXMgaXMgYXMgZm9sbG93czoKCiFbRmlndXJlIDMuIGFsbCBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXNdKGZpZ3VyZXMvZ3Byb2ZfZGUucG5nKQoKZzpQcm9maWxlciBoYXMgb21pdHRlZCB0aXRsZXMgZm9yIHRoZSBwbG90IGFuZCB0YWJsZSwgYnV0IGFwcHJvcHJpYXRlIG9uZXMgd291bGQgYmUgIk92ZXItcmVwcmVzZW50YXRpb24gb2Ygc2lnbmlmaWNhbnQKZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzIHdpdGggcC12YWx1ZSA8IDAuMDUiIGFuZCAiSGlnaGVzdCBzY29yaW5nIHBhdGh3YXlzIGZvciBnZW5lIHNldHMgd2l0aCBzaXplIGxlc3MgdGhhbgoyMDAiLCByZXNwZWN0aXZlbHkuCgpVc2luZyB0aGUgdGhyZXNob2xkcyBtZW50aW9uZWQgcHJldmlvdXNseSwgR086QlAgZm91bmQgMTMwIHBhdGh3YXlzLCBSRUFDIGZvdW5kIDM3LCBhbmQgV1AgZm91bmQgMTUuIFdoZW4gd2UgcHV0IGJvdGgKdGhlIHVwcmVndWxhdGVkIGdlbmVzIGFuZCBkb3ducmVndWxhdGVkIGdlbmVzIHRvZ2V0aGVyIHRocm91Z2ggb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgaXQgYXBwZWFycyB0aGF0IHdlIGdldCBhCmJldHRlciBvdmVyYWxsIGltYWdlIG9mIHRoZSBwYXRod2F5cyB0aGF0IEFEIGFmZmVjdHMuIEZvciBleGFtcGxlLCB0aGUgdG9wIEdPOkJQIGhpdCBpcyBub3cgZGlyZWN0bHkgcmVsYXRlZCB0byBza2luCmRldmVsb3BtZW50LCBhbiBvYnZpb3VzIGxpbmsgdG8gQUQuIEluIGFkZGl0aW9uLCB3ZSBzZWUgb3RoZXIgcGF0aHdheXMgcmVsYXRlZCB0byBpbW11bmUgZnVuY3Rpb247IGZvciBleGFtcGxlLCB0aGUKVG9sbC1saWtlIHJlY2VwdG9yIDQgKFRMUjQpIGNhc2NhZGUsIHdoaWNoIGlzIHJlc3BvbnNpYmxlIGZvciB0aGUgcHJvZHVjdGlvbiBvZiBjeXRva2luZXMuCgpTaW5jZSBtYW55IG9mIHRoZXNlIHBhdGh3YXlzIHdlcmUgbm90IHByZXNlbnQgd2hlbiBvbmx5IHRoZSB1cC0gb3IgZG93bnJlZ3VsYXRlZCBnZW5lcyB3ZXJlIGNvbnNpZGVyZWQsIGl0IGlzIHBvc3NpYmxlCnRvIGNvbmNsdWRlIHRoYXQgQUQgaXMgaW52b2x2ZWQgaW4gbW9yZSBjb21wbGV4IHBhdGh3YXlzIHRoYXQgY2Fubm90IGJlIHJlc3RyaWN0ZWQgdG8ganVzdCBvbmUgdHlwZSBvZiBnZW5lLiBUaGUgZW50aXJlCnNldCBvZiBkaWZmZXJlbnRpYWxseSBleHByZXNzZWQgZ2VuZXMgbmVlZHMgdG8gYmUgY29uc2lkZXJlZCwgYW5kIG9ubHkgdGhlbiB3aWxsIHRoZSByZWxldmFudCBwYXRod2F5cyBhcHBlYXIuCgojIyA0LiBJbnRlcnByZXRhdGlvbgoKV2l0aCByZWdhcmQgdG8gdGhlIGRpZmZlcmVudGlhbCBleHByZXNzaW9uIGFuYWx5c2lzLCBzZXZlcmFsIG9mIHRoZSBtb3N0IHNpZ25pZmljYW50IGdlbmVzIHRoYXQgd2VyZSBmb3VuZCB3ZXJlIGFsc28KZm91bmQgYnkgdGhlIGF1dGhvcnMgb2YgdGhlIG9yaWdpbmFsIHBhcGVyLiBGb3IgZXhhbXBsZSwgb25lIHdpdGggYSBwYXJ0aWN1bGFybHkgaGlnaCBsZXZlbCBvZiBleHByZXNzaW9uIHdhcyBJTDE5LAp3aXRoIHRoZSBwYXBlciBzdGF0aW5nIHRoYXQgdGhleSAiZGV0ZWN0ZWQgYSBub3RhYmxlIGV4cHJlc3Npb24gb2YgSUwxOSAuLi4gaW4gQUwiLCBleGFjdGx5IHdoYXQgd2FzIGZvdW5kIGhlcmUuCkhvd2V2ZXIsIHRoaXMgYW5hbHlzaXMgZGlkIGZpbmQgc29tZSBoaWdobHkgZXhwcmVzc2VkIGdlbmVzIHRoYXQgd2VyZSBub3Qgb2Ygbm90ZSBpbiB0aGUgcGFwZXI7IGZvciBleGFtcGxlLCBOUFRYMiwKcmVsYXRlZCB0byB0aGUgQy1yZWFjdGl2ZSBwcm90ZWluLCB3YXMgbm90IG5vdGVkIGluIHRoZSBwYXBlci4gQWRkaXRpb25hbGx5LCBvdGhlciBnZW5lcyBsaWtlIElMMzEgYW5kIElMMjMgdGhhdCB3ZXJlCm5vdGVkIGluIHRoZSBwYXBlciB3ZXJlIG5vdCBhbW9uZyB0aGUgbW9zdCBleHByZXNzZWQgZ2VuZXMgaGVyZS4gVGh1cywgd2UgY2FuIHNheSB0aGF0IGZvciB0aGlzIHBhcnQgb2YgdGhlIG5vdGVib29rLAp0aGUgcmVzdWx0cyBhcmUgYXQgbGVhc3QgcGFydGlhbGx5IHN1cHBvcnRlZCBieSB0aGUgb3JpZ2luYWwgcGFwZXIuCgpXaGVuIGxvb2tpbmcgYXQgcGF0aHdheXMsIHRoZSByZXN1bHQgb2Ygb3Zlci1yZXByZXNlbnRhdGlvbiBhbmFseXNpcywgd2Ugc2VlIHNvbWUgc2ltaWxhcml0aWVzIGJldHdlZW4gdGhvc2UgdGhhdCB3ZXJlCmZvdW5kIGhlcmUgYW5kIHRob3NlIHRoYXQgd2VyZSBmb3VuZCBpbiB0aGUgb3JpZ2luYWwgcGFwZXIuIFRoZSBwYXBlciBzdGF0ZXMgdGhhdCB0aGUgImNvcmUgdHJhbnNjcmlwdG9tZSBzaWduYXR1cmUiIG9mCkFEIHBhdGllbnRzIGlzIGNoYXJhY3Rlcml6ZWQgYnkgZXBpZGVybWFsIGRpZmZlcmVudGlhdGlvbiBhbmQgaXRjaCBwYXRod2F5cywgYW5kIGdpdmVzIHNlYmFjZW91cyBnbGFuZHMgYXMgYW4gZXhhbXBsZS4KSW4gdGhlIE9SQSBvZiBhbGwgZGlmZmVyZW50aWFsbHkgZXhwcmVzc2VkIGdlbmVzLCB0aGUgaGlnaGVzdCBzY29yZWQgcGF0aHdheSBpcyByZWxhdGVkIHRvIHNraW4gZGV2ZWxvcG1lbnQsIG9mIHdoaWNoCiJzZWJhY2VvdXMgZ2xhbmQgZGV2ZWxvcG1lbnQiIGlzIGEgY2hpbGQuCgpBZGRpdGlvbmFsIHBhdGh3YXlzIHRoYXQgd2VyZSBmb3VuZCBpbiBib3RoIGluY2x1ZGUgZ2VuZXJhbCBUb2xsLWxpa2UgcmVjZXB0b3Igc2lnbmFsbGluZywgaW50ZXJsZXVraW4gc2lnbmFsbGluZywgYW5kCnNldmVyYWwgb3RoZXIgaW1tdW5lLXJlbGF0ZWQgcGF0aHdheXMuIEhvd2V2ZXIsIHRoZSBwYXBlciBkb2VzIG1lbnRpb24gc3BlY2lmaWMgZXhhbXBsZXMgbGlrZSBUaDE3IGNlbGwgZGlmZmVyZW50aWF0aW9uCmFuZCB0aGUgSUwtMTcgc2lnbmFsbGluZyBwYXRod2F5LCB3aGljaCB3ZXJlIG5vdCBleGFjdGx5IGZvdW5kIGluIHRoZSBPUkEuIFRodXMsIHdlIHNob3VsZCBiZSBhYmxlIHRvIGNvbmNsdWRlIHRoYXQgZm9yCnRoaXMgcGFydCBvZiB0aGUgbm90ZWJvb2ssIHRoZSByZXN1bHRzIG92ZXJhbGwgYXJlIHN1cHBvcnRlZCBieSB0aGUgb3JpZ2luYWwgcGFwZXIsIGJ1dCBpbiBvcmRlciB0byBjb25maXJtIHRoZQpwcmVzZW5jZSBvZiBtb3JlIHNwZWNpZmljIHBhdGh3YXlzLCBtb3JlIGRldGFpbGVkIGV4YW1pbmF0aW9uIG11c3QgYmUgZG9uZS4KCldoZW4gY29uc2lkZXJpbmcgdGhlIGRpZmZlcmVudGlhbGx5IGV4cHJlc3NlZCBnZW5lcyBhbmQgcGF0aHdheXMsIGFub3RoZXIgd2F5IG9mIGxvb2tpbmcgZm9yIGV2aWRlbmNlIHRvIHN1cHBvcnQgdGhhdAp0aGUgYW5hbHlzZXMgaW4gdGhpcyBub3RlYm9vayAob3RoZXIgdGhhbiBjb21wYXJpbmcgd2l0aCB0aGUgb3JpZ2luYWwgcGFwZXIpIGlzIHRvIGxvb2sgZm9yIGFkZGl0aW9uYWwgcGFwZXJzIHRoYXQKaGF2ZSByZWFjaGVkIHRoZSBzYW1lIGNvbmNsdXNpb24uIE9uZSBzdHVkeSBbQG9rYV0gc2hvd2VkIHRoYXQgSUwtMTksIG9uZSBvZiB0aGUgbW9zdCBzaWduaWZpY2FudGx5IGV4cHJlc3NlZAppbnRlcmxldWtpbiBnZW5lcyBmb3VuZCBoZXJlLCB3YXMgbm90YWJseSBpbmNyZWFzZWQgaW4gcGF0aWVudHMgd2l0aCBBRC4gU3RhdGlzdGljYWwgYW5hbHlzaXMgKHByaW1hcmlseSBkb25lIHZpYSB0aGUKTWFubi1XaGl0bmV5IFUgdGVzdCkgd2FzIHBlcmZvcm1lZCwgYW5kIGluIHN1bW1hcnksIGNvbmNsdWRlZCB0aGF0IHRoZXJlIHdhcyBzdGF0aXN0aWNhbGx5IHNpZ25pZmljYW50IGV2aWRlbmNlIHRvCnN1Z2dlc3QgdGhhdCBBRCBwYXRpZW50cyBoYWQgZWxldmF0ZWQgSUwtMTkgcHJvdGVpbiBsZXZlbHMsIGEgcmVmbGVjdGlvbiBvZiBzaWduaWZpY2FudCBleHByZXNzaW9uLgoKQSBzZWNvbmQgcGFwZXIgW0BwYW56ZXJdIGZvdW5kIHRoYXQgQUQgcGF0aWVudHMgYWxzbyBoYXZlIHNpZ25pZmljYW50IGxldmVscyBvZiBUTFI0IGV4cHJlc3Npb24gYXMgcGFydCBvZgpUb2xsLWxpa2UgcmVjZXB0b3Igc2lnbmFsbGluZyBwYXRod2F5cy4gU2FtcGxlcyBmcm9tIGxlc2lvbmFsIHNraW4gd2VyZSB0YWtlbiwgaW5jdWJhdGVkIHdpdGggVExSNCBhbnRpYm9kaWVzLCBhbmQgdGhlbgpSTkEgd2FzIGV4dHJhY3RlZCwgd2hpY2ggd2FzIGZvdW5kIHRvIHNob3cgY29uc3RpdHV0aXZlIGV4cHJlc3Npb24gb2YgVExSNC4gVGhlcmUgaGF2ZSBldmVuIGJlZW4gcHJldmlvdXMgc3R1ZGllcyBvbgp1c2luZyBkdXBpbHVtYWIgYW5kIGN5Y2xvc3BvcmluZSB0byB0cmVhdCBBRDsgYSB0aGlkIHBhcGVyIFtAbWFuc291cmldIHNwZWNpZmljYWxseSBzdHVkaWVkIHRoZSBlZmZlY3RzIG9mIHRoZXNlIGRydWdzCm9uIGh1bWFuIGltbXVuZSBwYXRod2F5cywgYW5kIGZvdW5kIHRoYXQgdGhleSBhcmUgYWJsZSB0byBibG9jayBzb21lIGludGVybGV1a2luIHNpZ25hbGxpbmcgcGF0aHdheXMsIHdoaWNoIGluIHR1cm4KcmVzdWx0ZWQgaW4gY2xpbmljYWwgaW1wcm92ZW1lbnRzLiBUaHVzLCB0aGlzIHNob3dzIHRoYXQgdGhlcmUgaXMgZnVydGhlciBzdXBwb3J0IGZvciB0aGUgcGF0aHdheXMgZGlzY292ZXJlZCBieSB0aGUKT1JBIGNvbmR1Y3RlZCBoZXJlLgoKIyMgNS4gUmVmZXJlbmNlcwo=